From c6e3fcfb87619da9d840f35b5069f9a630aea1df Mon Sep 17 00:00:00 2001 From: Tony Lopez Date: Mon, 18 Mar 2019 10:57:43 -0500 Subject: [PATCH 1/3] adding ConfigPlaceholder support --- .../config/DefaultCoreBuilderConfig.java | 10 +- .../config/DefaultCoreExecutorConfig.java | 8 + .../cmd/exec/CommandExecutorGateway.java | 4 +- .../cmd/exec/ConfigPlaceholderResolver.java | 37 +++ .../DefaultConfigPlaceholderResolver.java | 62 ++++ .../DefaultCommandExecutorGateway.java | 45 +-- .../DefaultCommandPathVariableResolver.java | 266 ++++++++++++------ .../domain/cmd/exec/internal/Registry.java | 41 +++ .../internal/ReservedKeywordRegistry.java | 41 +++ .../oss/nimbus/domain/defn/Execution.java | 38 ++- .../validaton/ConfigPlaceholderValidator.java | 47 ++++ .../domain/model/config/ExecutionConfig.java | 60 ++++ .../internal/ExecutionConfigFactory.java | 24 +- .../internal/DefaultExecutionConfig.java | 40 ++- .../ConfigConditionalStateChangeHandler.java | 10 +- .../support/ExecutionConfigOrderTest.java | 5 +- .../s0/view/SampleConfigPlaceholders.java | 69 +++++ .../s0/view/VRSampleViewRootEntity.java | 2 + .../CommandPathVariableResolverTest.java | 134 ++++++++- 19 files changed, 803 insertions(+), 140 deletions(-) create mode 100644 nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ConfigPlaceholderResolver.java create mode 100644 nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigPlaceholderResolver.java create mode 100644 nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/Registry.java create mode 100644 nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/ReservedKeywordRegistry.java create mode 100644 nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/ConfigPlaceholderValidator.java create mode 100644 nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/SampleConfigPlaceholders.java diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreBuilderConfig.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreBuilderConfig.java index 0c1d129e8..c7cecbb91 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreBuilderConfig.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreBuilderConfig.java @@ -34,6 +34,7 @@ import com.antheminc.oss.nimbus.context.BeanResolverStrategy; import com.antheminc.oss.nimbus.context.DefaultBeanResolverStrategy; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.FunctionExecutor; +import com.antheminc.oss.nimbus.domain.cmd.exec.internal.ReservedKeywordRegistry; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.process.ParamUpdateEventListener; import com.antheminc.oss.nimbus.domain.config.builder.AnnotationAttributeHandler; import com.antheminc.oss.nimbus.domain.config.builder.AnnotationConfigHandler; @@ -125,8 +126,13 @@ public EventHandlerConfigFactory eventHandlerConfigFactory(BeanResolverStrategy } @Bean - public ExecutionConfigFactory executionConfigFactory(BeanResolverStrategy beanResolver) { - return new ExecutionConfigFactory(); + public ReservedKeywordRegistry reservedKeywordRegistry() { + return new ReservedKeywordRegistry(); + } + + @Bean + public ExecutionConfigFactory executionConfigFactory() { + return new ExecutionConfigFactory(reservedKeywordRegistry()); } @Bean diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreExecutorConfig.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreExecutorConfig.java index e3c0ba9e2..27a517b74 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreExecutorConfig.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreExecutorConfig.java @@ -24,8 +24,10 @@ import com.antheminc.oss.nimbus.domain.cmd.exec.CommandExecutor; import com.antheminc.oss.nimbus.domain.cmd.exec.CommandPathVariableResolver; import com.antheminc.oss.nimbus.domain.cmd.exec.CommandTransactionInterceptor; +import com.antheminc.oss.nimbus.domain.cmd.exec.DefaultConfigPlaceholderResolver; import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContextLoader; import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContextPathVariableResolver; +import com.antheminc.oss.nimbus.domain.cmd.exec.ConfigPlaceholderResolver; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.DefaultActionExecutorConfig; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.DefaultActionExecutorDelete; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.DefaultActionExecutorGet; @@ -41,6 +43,7 @@ import com.antheminc.oss.nimbus.domain.cmd.exec.internal.DefaultExecutionContextLoader; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.DefaultExecutionContextPathVariableResolver; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.HierarchyMatchBasedBeanFinder; +import com.antheminc.oss.nimbus.domain.cmd.exec.internal.ReservedKeywordRegistry; import com.antheminc.oss.nimbus.support.expr.ExpressionEvaluator; import com.antheminc.oss.nimbus.support.expr.SpelExpressionEvaluator; @@ -76,6 +79,11 @@ public ExecutionContextPathVariableResolver defaultExecutionContextPathVariableR return new DefaultExecutionContextPathVariableResolver(beanResolver); } + @Bean + public ConfigPlaceholderResolver paramPathPlaceholderResolver(ReservedKeywordRegistry reservedKeywordRegistry) { + return new DefaultConfigPlaceholderResolver(reservedKeywordRegistry); + } + @Bean(name="default.ExecutionContextLoader", destroyMethod="clear") //@Scope(proxyMode=ScopedProxyMode.TARGET_CLASS, scopeName="session") public ExecutionContextLoader defaultExecutionContextLoader(BeanResolverStrategy beanResolver) { diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/CommandExecutorGateway.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/CommandExecutorGateway.java index f1907ff6a..a470fc621 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/CommandExecutorGateway.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/CommandExecutorGateway.java @@ -15,12 +15,12 @@ */ package com.antheminc.oss.nimbus.domain.cmd.exec; -import java.lang.annotation.Annotation; import java.util.List; import com.antheminc.oss.nimbus.domain.cmd.Command; import com.antheminc.oss.nimbus.domain.cmd.CommandMessage; import com.antheminc.oss.nimbus.domain.cmd.exec.CommandExecution.MultiOutput; +import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig; import com.antheminc.oss.nimbus.domain.model.state.EntityState.Param; /** @@ -34,6 +34,6 @@ public interface CommandExecutorGateway { MultiOutput execute(CommandMessage cmdMsg); - List executeConfig(ExecutionContext eCtx, Param cmdParam, List execConfigs); + List executeConfig(ExecutionContext eCtx, Param cmdParam, ExecutionConfig exectionConfig); } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ConfigPlaceholderResolver.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ConfigPlaceholderResolver.java new file mode 100644 index 000000000..f48161f14 --- /dev/null +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ConfigPlaceholderResolver.java @@ -0,0 +1,37 @@ +/** + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.antheminc.oss.nimbus.domain.cmd.exec; + +import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig.Context; + +/** + * @author Tony Lopez + * @since 1.3 + * + */ +public interface ConfigPlaceholderResolver { + + /** + *

Resolve any config placeholders that are present in the the provided + * path and return the resolved result. + * @param context the context object from which to retrieve config + * placeholders + * @param pathToResolve the path to resolve against + * @return the resolved path + */ + String resolve(Context context, String pathToResolve); + +} diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigPlaceholderResolver.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigPlaceholderResolver.java new file mode 100644 index 000000000..6874ecd36 --- /dev/null +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigPlaceholderResolver.java @@ -0,0 +1,62 @@ +/** + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.antheminc.oss.nimbus.domain.cmd.exec; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; + +import com.antheminc.oss.nimbus.domain.cmd.exec.internal.ReservedKeywordRegistry; +import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig.Context; + +import lombok.RequiredArgsConstructor; + +/** + * @author Tony Lopez + * @since 1.3 + * + */ +@RequiredArgsConstructor +public class DefaultConfigPlaceholderResolver implements ConfigPlaceholderResolver { + + private final ReservedKeywordRegistry reservedKeywordRegistry; + + @Override + public String resolve(Context context, String pathToResolve) { + Map entries = ParamPathExpressionParser.parse(pathToResolve); + + // remove reserved placeholders -- those will be handled by another + // resolver + entries = entries.entrySet().stream().filter(x -> !this.reservedKeywordRegistry.exists(x.getValue())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + if (MapUtils.isEmpty(entries)) { + return pathToResolve; + } + + String out = pathToResolve; + for (Entry entry : entries.entrySet()) { + String placeholderName = ParamPathExpressionParser.stripPrefixSuffix(entry.getValue()); + String placeholderValue = context.getPlaceholderMap().get(placeholderName); + out = StringUtils.replace(out, entry.getValue(), placeholderValue, 1); + } + + return out; + } +} diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandExecutorGateway.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandExecutorGateway.java index d3182a5da..97f2fed3f 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandExecutorGateway.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandExecutorGateway.java @@ -17,7 +17,6 @@ import java.lang.annotation.Annotation; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -55,6 +54,7 @@ import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContextLoader; import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContextPathVariableResolver; import com.antheminc.oss.nimbus.domain.cmd.exec.ParamPathExpressionParser; +import com.antheminc.oss.nimbus.domain.cmd.exec.ConfigPlaceholderResolver; import com.antheminc.oss.nimbus.domain.config.builder.DomainConfigBuilder; import com.antheminc.oss.nimbus.domain.defn.Constants; import com.antheminc.oss.nimbus.domain.defn.Execution.Config; @@ -62,6 +62,7 @@ import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig; import com.antheminc.oss.nimbus.domain.model.config.ModelConfig; import com.antheminc.oss.nimbus.domain.model.config.builder.ExecutionConfigProvider; +import com.antheminc.oss.nimbus.domain.model.config.internal.DefaultExecutionConfig; import com.antheminc.oss.nimbus.domain.model.state.EntityState.ExecutionModel; import com.antheminc.oss.nimbus.domain.model.state.EntityState.Param; import com.antheminc.oss.nimbus.domain.model.state.ExecutionTxnContext; @@ -94,6 +95,8 @@ public class DefaultCommandExecutorGateway extends BaseCommandExecutorStrategies private CommandPathVariableResolver pathVariableResolver; + private ConfigPlaceholderResolver configPlaceholderResolver; + private ExecutionContextPathVariableResolver eCtxPathVariableResolver; private ExecutionContextLoader loader; @@ -120,6 +123,7 @@ public void initDependencies() { this.domainConfigBuilder = getBeanResolver().get(DomainConfigBuilder.class); this.cmdHandler = getBeanResolver().get(ChangeLogCommandEventHandler.class); this.expressionEvaluator = getBeanResolver().get(ExpressionEvaluator.class); + this.configPlaceholderResolver = getBeanResolver().get(ConfigPlaceholderResolver.class); } @@ -217,7 +221,7 @@ protected MultiOutput executeInternal(ExecutionContext eCtx, CommandMessage cmdM ExecutionConfig executionConfig = cmdParam != null ? cmdParam.getConfig().getExecutionConfig() : null; if(cmdMsg.getCommand().getAction() == Action._get && executionConfig != null && CollectionUtils.isNotEmpty(executionConfig.get())) { - List execConfigOutputs = executeConfig(eCtx, cmdParam, executionConfig.get()); + List execConfigOutputs = executeConfig(eCtx, cmdParam, executionConfig); execConfigOutputs.stream().forEach(mOut->addMultiOutput(mOutput, mOut)); } else {// otherwise, execute self @@ -237,12 +241,12 @@ protected void validateCommand(CommandMessage cmdMsg) { @SuppressWarnings("unchecked") @Override - public List executeConfig(ExecutionContext eCtx, Param cmdParam, List execConfigs) { + public List executeConfig(ExecutionContext eCtx, Param cmdParam, ExecutionConfig executionConfig) { final CommandMessage cmdMsg = eCtx.getCommandMessage(); boolean isPayloadUsed = false; final List configExecOutputs = new ArrayList<>(); - execConfigs.stream().forEach(ec-> { + executionConfig.get().stream().forEach(ec-> { final ExecutionConfigProvider execConfigProvider = getBeanResolver().get(ExecutionConfigProvider.class, ec.annotationType()); Config config = execConfigProvider.getMain(ec); try { @@ -251,14 +255,15 @@ public List executeConfig(ExecutionContext eCtx, Param cmdParam, return; if(StringUtils.isNotBlank(config.col())) { - buildAndExecuteColExecConfig(eCtx, cmdParam, config); + buildAndExecuteColExecConfig(eCtx, cmdParam, executionConfig, config); } else { - String completeConfigUri = eCtx.getCommandMessage().getCommand().getRelativeUri(config.url()); - String eCtxResolvedConfigUri = getECtxPathVariableResolver().resolve(eCtx, cmdParam, completeConfigUri); - String resolvedConfigUri = getPathVariableResolver().resolve(cmdParam, eCtxResolvedConfigUri); + String resolvedURL0 = eCtx.getCommandMessage().getCommand().getRelativeUri(config.url()); + String resolvedURL1 = getECtxPathVariableResolver().resolve(eCtx, cmdParam, resolvedURL0); + String resolvedURL2 = getConfigPlaceholderResolver().resolve(executionConfig.getContext(), resolvedURL1); + String resolvedURL3 = getPathVariableResolver().resolve(cmdParam, resolvedURL2); - Command configExecCmd = CommandBuilder.withUri(resolvedConfigUri).getCommand(); + Command configExecCmd = CommandBuilder.withUri(resolvedURL3).getCommand(); CommandMessage configCmdMsg = new CommandMessage(configExecCmd, resolvePayload(cmdMsg, configExecCmd, isPayloadUsed)); // execute & add output to mOutput @@ -272,7 +277,11 @@ public List executeConfig(ExecutionContext eCtx, Param cmdParam, throw ex; logit.error(() -> "Failed to execute main config "+ config +" on param "+ cmdParam +" , executing the onException config", ex); - executeConfig(eCtx, cmdParam, Arrays.asList(exceptionConfig)); + + DefaultExecutionConfig exceptionExecutionConfig = new DefaultExecutionConfig(); + exceptionExecutionConfig.add(exceptionConfig); + exceptionExecutionConfig.setContext(executionConfig.getContext()); + executeConfig(eCtx, cmdParam, exceptionExecutionConfig); } }); return configExecOutputs; @@ -313,7 +322,7 @@ private MultiOutput executeConfig(Command inputCmd, CommandMessage configCmdMsg) } } - private void buildAndExecuteColExecConfig(ExecutionContext eCtx, Param cmdParam, Config ec) { + private void buildAndExecuteColExecConfig(ExecutionContext eCtx, Param cmdParam, ExecutionConfig executionConfig, Config ec) { List colExecConfigs = new ArrayList<>(); String colPath = ParamPathExpressionParser.stripPrefixSuffix(ec.col()); @@ -324,8 +333,9 @@ private void buildAndExecuteColExecConfig(ExecutionContext eCtx, Param cmdPar if(p.isCollection()) { for(int i=0; i < p.findIfCollection().size(); i++) { - String url = StringUtils.replace(ec.url(),Constants.MARKER_COL_PARAM.code,colPath+Constants.SEPARATOR_URI.code+i); - colExecConfigs.add(ExecutionConfigBuilder.buildExecConfig(url, ec.order())); + String resolvedURL1 = getConfigPlaceholderResolver().resolve(executionConfig.getContext(), ec.url()); + String resolvedURL2 = StringUtils.replace(resolvedURL1,Constants.MARKER_COL_PARAM.code,colPath+Constants.SEPARATOR_URI.code+i); + colExecConfigs.add(ExecutionConfigBuilder.buildExecConfig(resolvedURL2, ec.order())); } } else if(p.getConfig().getType().isArray()) { @@ -336,12 +346,15 @@ else if(p.getConfig().getType().isArray()) { int size = ArrayUtils.getLength(arrayParamState); for(int i=0; i < size; i++) { - String url = StringUtils.replace(ec.url(), Constants.MARKER_COL_PARAM_EXPR.code, String.valueOf(arrayParamState[i])); - colExecConfigs.add(ExecutionConfigBuilder.buildExecConfig(url, ec.order())); + String resolvedURL1 = getConfigPlaceholderResolver().resolve(executionConfig.getContext(), ec.url()); + String resolvedURL2 = StringUtils.replace(resolvedURL1, Constants.MARKER_COL_PARAM_EXPR.code, String.valueOf(arrayParamState[i])); + colExecConfigs.add(ExecutionConfigBuilder.buildExecConfig(resolvedURL2, ec.order())); } } - executeConfig(eCtx, cmdParam, colExecConfigs); + DefaultExecutionConfig colExecutionConfig = new DefaultExecutionConfig(); + colExecConfigs.forEach(colExecutionConfig::add); + executeConfig(eCtx, cmdParam, colExecutionConfig); } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandPathVariableResolver.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandPathVariableResolver.java index 3fd04b3c8..909e0dd9e 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandPathVariableResolver.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandPathVariableResolver.java @@ -15,8 +15,10 @@ */ package com.antheminc.oss.nimbus.domain.cmd.exec.internal; +import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.regex.Matcher; @@ -49,6 +51,7 @@ import lombok.AccessLevel; import lombok.Getter; +import lombok.RequiredArgsConstructor; /** * @author Soham Chakravarti @@ -58,17 +61,126 @@ @Getter(value=AccessLevel.PROTECTED) public class DefaultCommandPathVariableResolver implements CommandPathVariableResolver { - protected final JustLogit logit = new JustLogit(DefaultCommandPathVariableResolver.class); + public class ElemIdResolver extends Resolver { + + public ElemIdResolver(String keyword) { + super(keyword); + } + + @Override + public String resolve(Param param, String pathToResolve) { + return mapColElem(param, pathToResolve); + } + + protected String mapColElem(Param param, String pathToResolve) { + // check if command param is colElem + if(param.isCollectionElem()) + return param.findIfCollectionElem().getElemId(); + + // otherwise, if mapped, check if mapsTo param is colElem + if(param.isMapped()) + return mapColElem(param.findIfMapped().getMapsTo(), pathToResolve); + + // throw ex ..or.. blank?? + return ""; + } + } + public class EnvResolver extends Resolver { + + public EnvResolver(String keyword) { + super(keyword); + } + + @Override + public String resolve(Param param, String pathToResolve) { + String property = pathToResolve.replace(Constants.MARKER_ENV.code+".", ""); + return environment.getProperty(property); + } + + } + public class PlatformMarkerResolver extends Resolver { + + public PlatformMarkerResolver(String keyword) { + super(keyword); + } + + @Override + public String resolve(Param param, String pathToResolve) { + return String.valueOf(mapCrossDomain(param, pathToResolve)); + } + + } + public class RefIdResolver extends Resolver { + + public RefIdResolver(String keyword) { + super(keyword); + } + + @Override + public String resolve(Param param, String pathToResolve) { + return String.valueOf(param.getRootExecution().getRootCommand().getRefId(Type.DomainAlias)); + } + + } + + @RequiredArgsConstructor + public abstract class Resolver { + + @Getter + private final String keyword; + + abstract String resolve(Param param, String pathToResolve); + + protected boolean shouldApply(String pathToResolve) { + return StringUtils.startsWithIgnoreCase(pathToResolve, this.keyword); + } + } + + public class SelfResolver extends Resolver { + + public SelfResolver(String keyword) { + super(keyword); + } + + @Override + public String resolve(Param param, String pathToResolve) { + if(StringUtils.endsWith(pathToResolve, "loginId")) + return Optional.ofNullable(getSessionProvider().getLoggedInUser()).orElseGet(() -> new ClientUser()).getLoginId(); + if(StringUtils.endsWith(pathToResolve, "id")) { + Long id = Optional.ofNullable(getSessionProvider().getLoggedInUser()).orElseGet(() -> new ClientUser()).getId(); + return String.valueOf(id); + } + + return param.getRootExecution().getRootCommand().getElementSafely(Type.ClientAlias).getAlias(); + } + + } + public class ThisResolver extends Resolver { + + public ThisResolver(String keyword) { + super(keyword); + } + + @Override + public String resolve(Param param, String pathToResolve) { + return StringUtils.removeStart(param.getPath(), param.getRootDomain().getPath()); + } + + } private static final String NULL_STRING = "null"; private static final String NULL_STRING_REGEX = "\\s*\"null\"\\s*"; private static final Pattern NULL_STRING_PATTERN = Pattern.compile(NULL_STRING_REGEX); private final CommandMessageConverter converter; - private final PropertyResolver propertyResolver; - private final SessionProvider sessionProvider; private final Environment environment; private final CommandExecutorGateway executorGateway; + private final PropertyResolver propertyResolver; + private final SessionProvider sessionProvider; + private final ReservedKeywordRegistry reservedKeywordRegistry; + private final List resolvers = new ArrayList<>(); + + protected final JustLogit logit = new JustLogit(DefaultCommandPathVariableResolver.class); public DefaultCommandPathVariableResolver(BeanResolverStrategy beanResolver, PropertyResolver propertyResolver) { this.converter = beanResolver.get(CommandMessageConverter.class); @@ -76,8 +188,24 @@ public DefaultCommandPathVariableResolver(BeanResolverStrategy beanResolver, Pro this.sessionProvider = beanResolver.get(SessionProvider.class); this.environment = beanResolver.get(Environment.class); this.executorGateway = beanResolver.find(CommandExecutorGateway.class); + this.reservedKeywordRegistry = beanResolver.find(ReservedKeywordRegistry.class); + + registerDefaultResolvers(); } + public void registerDefaultResolvers() { + registerResolver(new SelfResolver(Constants.MARKER_SESSION_SELF.code)); + registerResolver(new EnvResolver(Constants.MARKER_ENV.code)); + registerResolver(new ThisResolver(Constants.MARKER_COMMAND_PARAM_CURRENT_SELF.code)); + registerResolver(new RefIdResolver(Constants.MARKER_REF_ID.code)); + registerResolver(new ElemIdResolver(Constants.MARKER_ELEM_ID.code)); + registerResolver(new PlatformMarkerResolver(Constants.SEGMENT_PLATFORM_MARKER.code)); + } + + public void registerResolver(Resolver resolver) { + this.reservedKeywordRegistry.register(resolver.getKeyword()); + this.resolvers.add(resolver); + } @Override public String resolve(Param param, String urlToResolve) { @@ -92,72 +220,47 @@ public String resolve(Param param, String urlToResolve) { throw new InvalidConfigException("Failed to resolve with property place-holders for param: "+param+" with url: "+urlToResolve, ex); } } - - - protected String resolveInternal(Param param, String urlToResolve) { - Map entries = ParamPathExpressionParser.parse(urlToResolve); - if(MapUtils.isEmpty(entries)) - return urlToResolve; - - String out = urlToResolve; - for(Integer i : entries.keySet()) { - String key = entries.get(i); - - // look for relative path to passed in param's parent model - String pathToResolve = ParamPathExpressionParser.stripPrefixSuffix(key); - - String val = map(param, pathToResolve); - - out = StringUtils.replace(out, key, val, 1); - } - - Matcher m = NULL_STRING_PATTERN.matcher(out); - out = m.replaceAll(NULL_STRING); // replaces all json="null" (including leading/trailing spaces) to json=null - return out; - } - + protected String map(Param param, String pathToResolve) { // handle recursive if(ParamPathExpressionParser.containsPrefixSuffix(pathToResolve)) { String recursedPath = resolve(param, pathToResolve); pathToResolve = recursedPath; } - - if(StringUtils.startsWithIgnoreCase(pathToResolve, Constants.MARKER_SESSION_SELF.code)) - return mapSelf(param, pathToResolve); - if(StringUtils.startsWithIgnoreCase(pathToResolve, Constants.MARKER_ENV.code)) - return mapEnvironment(param, pathToResolve); - - if(StringUtils.startsWithIgnoreCase(pathToResolve, Constants.MARKER_COMMAND_PARAM_CURRENT_SELF.code)) - return StringUtils.removeStart(param.getPath(), param.getRootDomain().getPath()); - if(StringUtils.startsWithIgnoreCase(pathToResolve, Constants.MARKER_REF_ID.code)) - return String.valueOf(param.getRootExecution().getRootCommand().getRefId(Type.DomainAlias)); - - if(StringUtils.startsWithIgnoreCase(pathToResolve, Constants.MARKER_ELEM_ID.code)) - return mapColElem(param, pathToResolve); - - if(StringUtils.startsWithIgnoreCase(pathToResolve, Constants.SEGMENT_PLATFORM_MARKER.code)) - return String.valueOf(mapCrossDomain(param, pathToResolve)); + for(Resolver resolver: resolvers) { + if (resolver.shouldApply(pathToResolve)) { + return resolver.resolve(param, pathToResolve); + } + } return mapQuad(param, pathToResolve); } - //TODO bean path evaluation to get value - protected String mapSelf(Param param, String pathToResolve) { - if(StringUtils.endsWith(pathToResolve, "loginId")) - return Optional.ofNullable(getSessionProvider().getLoggedInUser()).orElseGet(() -> new ClientUser()).getLoginId(); - if(StringUtils.endsWith(pathToResolve, "id")) { - Long id = Optional.ofNullable(getSessionProvider().getLoggedInUser()).orElseGet(() -> new ClientUser()).getId(); - return String.valueOf(id); + /** + * + * @param commandParam + * @param pathToResolve + * @return + * @since 1.1.11 + */ + protected Object mapCrossDomain(Param commandParam, String pathToResolve) { + Command rootCmd = commandParam.getRootExecution().getRootCommand(); + CommandBuilder cmdBuilder = CommandBuilder.withPlatformRelativePath(rootCmd, Type.AppAlias, pathToResolve); + cmdBuilder.setAction(Action._get); + cmdBuilder.setBehaviors(new LinkedList<>(Arrays.asList(Behavior.$state))); + Command cmd = cmdBuilder.getCommand(); + CommandMessage cmdMsg = new CommandMessage(cmd, null); + MultiOutput output = executorGateway.execute(cmdMsg); + Object response = output.getSingleResult(); + + if (response == null) { + logit.error(() -> new StringBuffer().append(" Param (using paramPath) [").append(pathToResolve).append("] not found from param: ").append(commandParam).toString()); + return null; } - return param.getRootExecution().getRootCommand().getElementSafely(Type.ClientAlias).getAlias(); - } - - protected String mapEnvironment(Param param, String pathToResolve) { - String property = pathToResolve.replace(Constants.MARKER_ENV.code+".", ""); - return environment.getProperty(property); + logit.debug(() -> new StringBuffer().append(" Param (using paramPath) [").append(pathToResolve).append("] has been resolved to ").append(response).toString()); + return response; } protected String mapQuad(Param param, String pathToResolve) { @@ -185,43 +288,26 @@ protected String mapQuad(Param param, String pathToResolve) { return String.valueOf(p.getState()); } } - - protected String mapColElem(Param commandParam, String pathToResolve) { - // check if command param is colElem - if(commandParam.isCollectionElem()) - return commandParam.findIfCollectionElem().getElemId(); - - // otherwise, if mapped, check if mapsTo param is colElem - if(commandParam.isMapped()) - return mapColElem(commandParam.findIfMapped().getMapsTo(), pathToResolve); - - // throw ex ..or.. blank?? - return ""; - } - /** - * - * @param commandParam - * @param pathToResolve - * @return - * @since 1.1.11 - */ - protected Object mapCrossDomain(Param commandParam, String pathToResolve) { - Command rootCmd = commandParam.getRootExecution().getRootCommand(); - CommandBuilder cmdBuilder = CommandBuilder.withPlatformRelativePath(rootCmd, Type.AppAlias, pathToResolve); - cmdBuilder.setAction(Action._get); - cmdBuilder.setBehaviors(new LinkedList<>(Arrays.asList(Behavior.$state))); - Command cmd = cmdBuilder.getCommand(); - CommandMessage cmdMsg = new CommandMessage(cmd, null); - MultiOutput output = executorGateway.execute(cmdMsg); - Object response = output.getSingleResult(); + protected String resolveInternal(Param param, String urlToResolve) { + Map entries = ParamPathExpressionParser.parse(urlToResolve); + if(MapUtils.isEmpty(entries)) + return urlToResolve; - if (response == null) { - logit.error(() -> new StringBuffer().append(" Param (using paramPath) [").append(pathToResolve).append("] not found from param: ").append(commandParam).toString()); - return null; + String out = urlToResolve; + for(Integer i : entries.keySet()) { + String key = entries.get(i); + + // look for relative path to passed in param's parent model + String pathToResolve = ParamPathExpressionParser.stripPrefixSuffix(key); + + String val = map(param, pathToResolve); + + out = StringUtils.replace(out, key, val, 1); } - logit.debug(() -> new StringBuffer().append(" Param (using paramPath) [").append(pathToResolve).append("] has been resolved to ").append(response).toString()); - return response; + Matcher m = NULL_STRING_PATTERN.matcher(out); + out = m.replaceAll(NULL_STRING); // replaces all json="null" (including leading/trailing spaces) to json=null + return out; } } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/Registry.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/Registry.java new file mode 100644 index 000000000..138f227e4 --- /dev/null +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/Registry.java @@ -0,0 +1,41 @@ +/** + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.antheminc.oss.nimbus.domain.cmd.exec.internal; + +/** + *

A simple registry implementation + * + * @author Tony Lopez + * + */ +public interface Registry { + + /** + *

Register the provided value within the registry. + * + * @param value the value to register + * @return {@code true} if the value was successfully registered + */ + boolean register(T value); + + /** + *

Determine wither or not the value exists within the registry. + * + * @param value the value to check whether or not it is in the registry + * @return {@code true} if the value exists within the registry + */ + boolean exists(T value); +} diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/ReservedKeywordRegistry.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/ReservedKeywordRegistry.java new file mode 100644 index 000000000..2950f66fc --- /dev/null +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/ReservedKeywordRegistry.java @@ -0,0 +1,41 @@ +/** + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.antheminc.oss.nimbus.domain.cmd.exec.internal; + +import java.util.HashSet; +import java.util.Set; + +/** + *

A registry for keeping track of the Command DSL reserved keywords in the framework. + * + * @author Tony Lopez + * + */ +public class ReservedKeywordRegistry implements Registry { + + private Set keywords = new HashSet<>(); + + @Override + public boolean register(String value) { + return this.keywords.add(value); + } + + @Override + public boolean exists(String value) { + return this.keywords.contains(value); + } + +} diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/Execution.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/Execution.java index 56f6ca015..d0e5d367f 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/Execution.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/Execution.java @@ -24,10 +24,10 @@ import org.apache.commons.lang3.StringUtils; +import com.antheminc.oss.nimbus.InvalidConfigException; import com.antheminc.oss.nimbus.domain.Event; import com.antheminc.oss.nimbus.domain.defn.Executions.Configs; import com.antheminc.oss.nimbus.domain.defn.Executions.DetourConfigs; -import com.antheminc.oss.nimbus.InvalidConfigException; /** * @author Soham Chakravarti @@ -108,4 +108,40 @@ int order() default Event.DEFAULT_ORDER_NUMBER; } + /** + *

Annotation which indicates that a command template variable should + * be bound to a provided value. Supported for {@link Config} annotated + * fields during the execution step. + * + *

The command template variable must not match any reserved template + * variables. + * + * @author Tony Lopez + * @since 1.3 + * @see com.antheminc.oss.nimbus.domain.cmd.exec.ConfigPlaceholderResolver + */ + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.FIELD }) + @Repeatable(ConfigPlaceholders.class) + @Execution + public @interface ConfigPlaceholder { + + /** + *

The name of the placeholder that should be used as an identifier to + * resolve in a config url. + */ + String name(); + + /** + *

The value to use in place of {@link #name()} when this config placeholder is resolved. + */ + String value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.FIELD }) + @Execution + public @interface ConfigPlaceholders { + ConfigPlaceholder[] value(); + } } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/ConfigPlaceholderValidator.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/ConfigPlaceholderValidator.java new file mode 100644 index 000000000..faf135cc9 --- /dev/null +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/ConfigPlaceholderValidator.java @@ -0,0 +1,47 @@ +/** + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.antheminc.oss.nimbus.domain.defn.validaton; + +import com.antheminc.oss.nimbus.InvalidConfigException; +import com.antheminc.oss.nimbus.domain.cmd.exec.internal.ReservedKeywordRegistry; +import com.antheminc.oss.nimbus.domain.defn.Execution.ConfigPlaceholder; + +import lombok.RequiredArgsConstructor; + +/** + *

Validator for {@link com.antheminc.oss.nimbus.domain.defn.Execution.ConfigPlaceholder} + * + * @author Tony Lopez + * @since 1.3 + */ +@RequiredArgsConstructor +public class ConfigPlaceholderValidator { + + private final ReservedKeywordRegistry reservedKeywordRegistry; + + public boolean supports(Class clazz) { + return ConfigPlaceholder.class.isAssignableFrom(clazz); + } + + public void validate(Object target) { + ConfigPlaceholder obj = (ConfigPlaceholder) target; + if (this.reservedKeywordRegistry.exists(obj.name())) { + throw new InvalidConfigException("The placeholder \"" + obj.name() + + "\" is a reserved keyword. Replacing reserved placeholders is not allowed."); + } + } + +} diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/ExecutionConfig.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/ExecutionConfig.java index 4059a347b..c0bae6538 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/ExecutionConfig.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/ExecutionConfig.java @@ -4,7 +4,13 @@ package com.antheminc.oss.nimbus.domain.model.config; import java.lang.annotation.Annotation; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import com.antheminc.oss.nimbus.domain.defn.Execution.ConfigPlaceholder; /** * @author Rakesh Patel @@ -14,4 +20,58 @@ public interface ExecutionConfig { public List get(); + /** + *

Retrieve the context details for this execution config instance. + * @return the context + */ + public Context getContext(); + + /** + *

A context object containing details related to an execution config + * instance. Data stored here should be used to support execution + * instructions. + * @author Tony Lopez + * @since 1.3 + */ + public static class Context { + private final Map placeholders; + + public Context() { + this.placeholders = new HashMap<>(); + } + + /** + *

Add a placeholder to the context. + * @param placeholder the placeholder to add + */ + public void addPlaceholder(ConfigPlaceholder placeholder) { + this.placeholders.put(placeholder.name(), placeholder.value()); + } + + /** + *

Add placeholders to the context. + * @param placeholders the placeholder to add + */ + public void addPlaceholders(ConfigPlaceholder[] placeholders) { + Stream.of(placeholders).forEach(this::addPlaceholder); + } + + /** + *

Retrieve a placeholder value by it's name. + * @param placeholderName the name of the placeholder for which to + * retrieve + */ + public String getPlaceholder(String placeholderName) { + return this.placeholders.get(placeholderName); + } + + /** + *

Get the map representation of the placeholders, with the key being + * the placeholder name and the value being the value to resolve the key + * to. + */ + public Map getPlaceholderMap() { + return Collections.unmodifiableMap(this.placeholders); + } + } } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ExecutionConfigFactory.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ExecutionConfigFactory.java index 97e11b28a..6da791c15 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ExecutionConfigFactory.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ExecutionConfigFactory.java @@ -5,9 +5,13 @@ import java.lang.reflect.AnnotatedElement; +import com.antheminc.oss.nimbus.domain.cmd.exec.internal.ReservedKeywordRegistry; import com.antheminc.oss.nimbus.domain.defn.Execution.Config; +import com.antheminc.oss.nimbus.domain.defn.Execution.ConfigPlaceholder; import com.antheminc.oss.nimbus.domain.defn.Execution.DetourConfig; +import com.antheminc.oss.nimbus.domain.defn.validaton.ConfigPlaceholderValidator; import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig; +import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig.Context; import com.antheminc.oss.nimbus.domain.model.config.internal.DefaultExecutionConfig; /** @@ -16,18 +20,30 @@ */ public class ExecutionConfigFactory { + private final ConfigPlaceholderValidator placeholderValidator; + + public ExecutionConfigFactory(ReservedKeywordRegistry reservedKeywordRegistry) { + this.placeholderValidator = new ConfigPlaceholderValidator(reservedKeywordRegistry); + } + public ExecutionConfig build(AnnotatedElement aElem) { - final DefaultExecutionConfig executionConfig = new DefaultExecutionConfig(); - final Config arr[] = aElem.getAnnotationsByType(Config.class); final DetourConfig arr2[] = aElem.getAnnotationsByType(DetourConfig.class); + DefaultExecutionConfig executionConfig = new DefaultExecutionConfig(); executionConfig.addAll(arr); executionConfig.addAll(arr2); - executionConfig.sort(); - return executionConfig; + Context context = new Context(); + final ConfigPlaceholder[] placeholders = aElem.getAnnotationsByType(ConfigPlaceholder.class); + for(ConfigPlaceholder placeholder : placeholders) { + placeholderValidator.validate(placeholder); + context.addPlaceholder(placeholder); + } + executionConfig.setContext(context); + + return executionConfig; } } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/internal/DefaultExecutionConfig.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/internal/DefaultExecutionConfig.java index d10143a2e..13bd9464e 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/internal/DefaultExecutionConfig.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/internal/DefaultExecutionConfig.java @@ -14,27 +14,43 @@ import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + /** * @author Rakesh Patel * */ +@AllArgsConstructor public class DefaultExecutionConfig implements ExecutionConfig { - + + @Getter @Setter + private Context context; private List execConfigs; + public DefaultExecutionConfig() { + this.context = new Context(); + this.execConfigs = new ArrayList<>(); + } + + public void add(Annotation configAnnotation) { + execConfigs.add(configAnnotation); + } + public void addAll(Annotation[] configAnnotations) { - if(execConfigs == null) { - execConfigs = new ArrayList(); - } execConfigs.addAll(Arrays.asList(configAnnotations)); - - } - + + @Override + public List get() { + return execConfigs; + } + public void sort() { - if(CollectionUtils.isEmpty(execConfigs)) + if (CollectionUtils.isEmpty(execConfigs)) return; - + Collections.sort(execConfigs, (o1, o2) -> { Integer order1 = (Integer) AnnotationUtils.getAnnotationAttributes(o1).get("order"); Integer order2 = (Integer) AnnotationUtils.getAnnotationAttributes(o2).get("order"); @@ -42,10 +58,4 @@ public void sort() { return order1.compareTo(order2); }); } - - @Override - public List get() { - return execConfigs; - } - } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/state/extension/ConfigConditionalStateChangeHandler.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/state/extension/ConfigConditionalStateChangeHandler.java index c9326abd4..8f9e2168b 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/state/extension/ConfigConditionalStateChangeHandler.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/state/extension/ConfigConditionalStateChangeHandler.java @@ -16,8 +16,6 @@ package com.antheminc.oss.nimbus.domain.model.state.extension; -import java.util.Arrays; - import org.apache.commons.lang3.ArrayUtils; import com.antheminc.oss.nimbus.InvalidConfigException; @@ -28,8 +26,7 @@ import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContextLoader; import com.antheminc.oss.nimbus.domain.defn.Execution.Config; import com.antheminc.oss.nimbus.domain.defn.extension.ConfigConditional; -import com.antheminc.oss.nimbus.domain.model.state.ExecutionTxnContext; -import com.antheminc.oss.nimbus.domain.model.state.ParamEvent; +import com.antheminc.oss.nimbus.domain.model.config.internal.DefaultExecutionConfig; import com.antheminc.oss.nimbus.domain.model.state.EntityState.Param; import com.antheminc.oss.nimbus.support.EnableLoggingInterceptor; @@ -82,7 +79,10 @@ protected void handleInternal(Param onChangeParam, ConfigConditional configur Command rootCmd = onChangeParam.getRootExecution().getRootCommand(); ExecutionContext eCtx = getContextLoader().load(rootCmd); - getCommandGateway().executeConfig(eCtx, onChangeParam, Arrays.asList(configs)); + DefaultExecutionConfig executionConfig = new DefaultExecutionConfig(); + executionConfig.addAll(configs); + executionConfig.setContext(onChangeParam.getConfig().getExecutionConfig().getContext()); + getCommandGateway().executeConfig(eCtx, onChangeParam, executionConfig); } // @Override diff --git a/nimbus-core/src/test/java/com/antheminc/oss/nimbus/support/ExecutionConfigOrderTest.java b/nimbus-core/src/test/java/com/antheminc/oss/nimbus/support/ExecutionConfigOrderTest.java index b10a9a1ea..8ad906683 100644 --- a/nimbus-core/src/test/java/com/antheminc/oss/nimbus/support/ExecutionConfigOrderTest.java +++ b/nimbus-core/src/test/java/com/antheminc/oss/nimbus/support/ExecutionConfigOrderTest.java @@ -11,6 +11,7 @@ import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.ReflectionUtils; +import com.antheminc.oss.nimbus.domain.cmd.exec.internal.ReservedKeywordRegistry; import com.antheminc.oss.nimbus.domain.defn.Execution.Config; import com.antheminc.oss.nimbus.domain.defn.Execution.DetourConfig; import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig; @@ -27,7 +28,7 @@ public class ExecutionConfigOrderTest { @Test public void t00() { - ExecutionConfigFactory factory = new ExecutionConfigFactory(); + ExecutionConfigFactory factory = new ExecutionConfigFactory(new ReservedKeywordRegistry()); ReflectionUtils.doWithFields(TestContainer.class, f -> { ExecutionConfig execCfg = factory.build(f); @@ -38,7 +39,7 @@ public void t00() { @Test public void t01() { - ExecutionConfigFactory factory = new ExecutionConfigFactory(); + ExecutionConfigFactory factory = new ExecutionConfigFactory(new ReservedKeywordRegistry()); ReflectionUtils.doWithFields(TestContainer.class, f -> { if(f.getName().contains("fieldWithBothConfigs")) { diff --git a/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/SampleConfigPlaceholders.java b/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/SampleConfigPlaceholders.java new file mode 100644 index 000000000..3f3f38cc6 --- /dev/null +++ b/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/SampleConfigPlaceholders.java @@ -0,0 +1,69 @@ +/** + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.antheminc.oss.nimbus.test.scenarios.s0.view; + +import java.util.List; + +import com.antheminc.oss.nimbus.domain.defn.Execution.Config; +import com.antheminc.oss.nimbus.domain.defn.Execution.ConfigPlaceholder; +import com.antheminc.oss.nimbus.domain.defn.Execution.DetourConfig; +import com.antheminc.oss.nimbus.domain.defn.Model; +import com.antheminc.oss.nimbus.domain.defn.extension.ConfigConditional; + +import lombok.Getter; +import lombok.Setter; + +/** + * @author Tony Lopez + * + */ +@Model +@Getter @Setter +public class SampleConfigPlaceholders { + + private String p0; + + @ConfigPlaceholder(name = "foo", value = "/../p0") + @Config(url = "/_process?fn=_set&value=p1-did-it") + private String p1; + + @ConfigPlaceholder(name = "bar", value = "/../p0") + @ConfigPlaceholder(name = "baz", value = "p2-did-it") + @Config(url = "/_process?fn=_set&value=") + private String p2; + + @Config(url = "/_process?fn=_set&value=p3-did-it") + private String p3; + + @ConfigPlaceholder(name = "foo", value = "/../p0") + @DetourConfig(main = @Config(url = "break things"), onException = @Config(url = "/_process?fn=_set&value=p4-did-it")) + private String p4; + + @ConfigPlaceholder(name = "foo", value = "/../p0") + @ConfigConditional(when = "true", config = { + @Config(url = "/_process?fn=_set&value=p5-did-it") + }) + private String p5; + + private String p6; + + private String[] p7; + + @ConfigPlaceholder(name = "foo", value = "/../p") + @Config(url = "/../p7/_replace?rawPayload=[\"0\"]") + @Config(url = "/_process?fn=_set&value=p8-did-it", col="") + private String p8; +} diff --git a/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/VRSampleViewRootEntity.java b/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/VRSampleViewRootEntity.java index cc30eaf6d..4d2acfbfe 100644 --- a/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/VRSampleViewRootEntity.java +++ b/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/VRSampleViewRootEntity.java @@ -62,4 +62,6 @@ public class VRSampleViewRootEntity { private List attr_list_2_simple; private SampleParamStateHolders paramStateHolders; + + private SampleConfigPlaceholders configPlaceholders; } diff --git a/nimbus-test/src/test/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/CommandPathVariableResolverTest.java b/nimbus-test/src/test/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/CommandPathVariableResolverTest.java index 09aadc357..b9cd8d6ed 100644 --- a/nimbus-test/src/test/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/CommandPathVariableResolverTest.java +++ b/nimbus-test/src/test/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/CommandPathVariableResolverTest.java @@ -18,16 +18,23 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import java.lang.annotation.Annotation; + import org.junit.FixMethodOrder; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.runners.MethodSorters; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.PropertyResolver; import org.springframework.mock.web.MockHttpServletRequest; import com.antheminc.oss.nimbus.domain.AbstractFrameworkIngerationPersistableTests; import com.antheminc.oss.nimbus.domain.cmd.Action; +import com.antheminc.oss.nimbus.domain.cmd.exec.CommandExecution.MultiOutput; +import com.antheminc.oss.nimbus.domain.cmd.exec.CommandExecutorGateway; +import com.antheminc.oss.nimbus.domain.defn.Execution.ConfigPlaceholder; import com.antheminc.oss.nimbus.domain.model.state.EntityState.Param; +import com.antheminc.oss.nimbus.support.Holder; import com.antheminc.oss.nimbus.test.domain.support.utils.ExtractResponseOutputUtils; import com.antheminc.oss.nimbus.test.domain.support.utils.MockHttpRequestBuilder; @@ -37,9 +44,12 @@ */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class CommandPathVariableResolverTest extends AbstractFrameworkIngerationPersistableTests { - + @Autowired - private PropertyResolver pr; + private CommandExecutorGateway commandExecutorGateway; + + @Rule + public ExpectedException thrown = ExpectedException.none(); @Test public void t0_nestedConfigPath() { @@ -93,7 +103,105 @@ public void t1_environmentProperty() { } + + @SuppressWarnings("unchecked") + @Test + public void testConfigPlaceholderSingle() { + Long refId = createOrGetDomainRoot_RefId(); + MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) + .addRefId(refId) + .addNested("/configPlaceholders/p1") + .addAction(Action._get) + .getMock(); + Holder resp = (Holder) controller.handleGet(req, null); + Param p0 = (Param) resp.getState().findFirstParamOutputEndingWithPath("/p0").getValue(); + + assertEquals("p1-did-it", p0.getState()); + } + + @SuppressWarnings("unchecked") + @Test + public void testConfigPlaceholderMultiple() { + Long refId = createOrGetDomainRoot_RefId(); + MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) + .addRefId(refId) + .addNested("/configPlaceholders/p2") + .addAction(Action._get) + .getMock(); + Holder resp = (Holder) controller.handleGet(req, null); + Param p0 = (Param) resp.getState().findFirstParamOutputEndingWithPath("/p0").getValue(); + + assertEquals("p2-did-it", p0.getState()); + } + + @Test + public void testConfigPlaceholderUndefined() { + thrown.expect(NumberFormatException.class); + thrown.expectMessage("For input string: \"1null\""); + + Long refId = createOrGetDomainRoot_RefId(); + MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) + .addRefId(refId) + .addNested("/configPlaceholders/p3") + .addAction(Action._get) + .getMock(); + + controller.handleGet(req, null); + } + + @SuppressWarnings("unchecked") + @Test + public void testConfigPlaceholderWithDetourConfigs() { + Long refId = createOrGetDomainRoot_RefId(); + MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) + .addRefId(refId) + .addNested("/configPlaceholders/p4") + .addAction(Action._get) + .getMock(); + + Holder resp = (Holder) controller.handleGet(req, null); + // TODO Output not generated. Why? + Param p0 = resp.getState().getContext().getQuadModel().getView().findParamByPath("/configPlaceholders/p0"); + + assertEquals("p4-did-it", p0.getState()); + } + + @SuppressWarnings("unchecked") + @Test + public void testConfigPlaceholderWithConfigConditional() { + Long refId = createOrGetDomainRoot_RefId(); + MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) + .addRefId(refId) + .addNested("/configPlaceholders/p5") + .addAction(Action._get) + .getMock(); + + Holder resp = (Holder) controller.handleGet(req, null); + Param p5 = (Param) resp.getState().findFirstParamOutputEndingWithPath("/p5").getValue(); + + p5.setState("changeit"); + + Param p0 = p5.findParamByPath("/../p0"); + assertEquals("p5-did-it", p0.getState()); + } + @SuppressWarnings("unchecked") + @Test + public void testConfigPlaceholderWithCol() { + Long refId = createOrGetDomainRoot_RefId(); + MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) + .addRefId(refId) + .addNested("/configPlaceholders/p8") + .addAction(Action._get) + .getMock(); + + Holder resp = (Holder) controller.handleGet(req, null); + // TODO Output not generated. Why? + Param p0 = resp.getState().getContext().getQuadModel().getView().findParamByPath("/configPlaceholders/p0"); + + assertEquals("p8-did-it", p0.getState()); + } + private void execGet(Long refId, String nestedPath) { final MockHttpServletRequest req = MockHttpRequestBuilder.withUri(CORE_NESTED_CONFIG_ROOT) .addRefId(refId) @@ -115,4 +223,24 @@ private Param excuteNewConfig() { Param p = ExtractResponseOutputUtils.extractOutput(resp); return p; } + + private static ConfigPlaceholder buildConfigPlaceholder(String name, String value) { + return new ConfigPlaceholder() { + + @Override + public Class annotationType() { + return ConfigPlaceholder.class; + } + + @Override + public String value() { + return value; + } + + @Override + public String name() { + return name; + } + }; + } } From 75eea4df33dd089ddb7a188a5816807d6808746c Mon Sep 17 00:00:00 2001 From: Tony Lopez Date: Tue, 26 Mar 2019 10:51:32 -0500 Subject: [PATCH 2/3] refactor: in progress --- .../config/DefaultCoreBuilderConfig.java | 6 + .../config/DefaultCoreExecutorConfig.java | 8 +- .../config/DefaultProcessConfig.java | 6 + .../cmd/exec/CommandExecutorGateway.java | 4 +- ...olver.java => ConfigVariableResolver.java} | 10 +- ...ava => DefaultConfigVariableResolver.java} | 14 +-- .../domain/cmd/exec/ExecutionContext.java | 41 ++++++- .../DefaultCommandExecutorGateway.java | 53 ++++----- .../DefaultCommandPathVariableResolver.java | 58 ++++++---- .../ConfigVariableFunctionHandler.java | 103 ++++++++++++++++++ .../oss/nimbus/domain/defn/Execution.java | 69 +++++++----- .../oss/nimbus/domain/defn/Executions.java | 7 ++ ...ator.java => ConfigVariableValidator.java} | 8 +- .../domain/model/config/ExecutionConfig.java | 61 ----------- .../builder/ExecutionConfigProvider.java | 44 +++++++- ...ConfigVariableExecutionConfigProvider.java | 46 ++++++++ .../DefaultExecutionConfigProvider.java | 19 +++- .../DetourExecutionConfigProvider.java | 19 +++- .../internal/ExecutionConfigFactory.java | 66 +++++++---- .../internal/DefaultExecutionConfig.java | 53 ++++++--- .../ConfigConditionalStateChangeHandler.java | 8 +- ...olders.java => SampleConfigVariables.java} | 18 ++- .../s0/view/VRSampleViewRootEntity.java | 2 +- .../CommandPathVariableResolverTest.java | 46 ++------ 24 files changed, 499 insertions(+), 270 deletions(-) rename nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/{ConfigPlaceholderResolver.java => ConfigVariableResolver.java} (76%) rename nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/{DefaultConfigPlaceholderResolver.java => DefaultConfigVariableResolver.java} (75%) create mode 100644 nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/process/ConfigVariableFunctionHandler.java rename nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/{ConfigPlaceholderValidator.java => ConfigVariableValidator.java} (86%) create mode 100644 nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ConfigVariableExecutionConfigProvider.java rename nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/{SampleConfigPlaceholders.java => SampleConfigVariables.java} (78%) diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreBuilderConfig.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreBuilderConfig.java index c7cecbb91..6fbffe0a6 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreBuilderConfig.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreBuilderConfig.java @@ -45,6 +45,7 @@ import com.antheminc.oss.nimbus.domain.config.builder.attributes.DefaultAnnotationAttributeHandler; import com.antheminc.oss.nimbus.domain.model.config.EntityConfig.Scope; import com.antheminc.oss.nimbus.domain.model.config.builder.EntityConfigBuilder; +import com.antheminc.oss.nimbus.domain.model.config.builder.internal.ConfigVariableExecutionConfigProvider; import com.antheminc.oss.nimbus.domain.model.config.builder.internal.DefaultEntityConfigBuilder; import com.antheminc.oss.nimbus.domain.model.config.builder.internal.DefaultExecutionConfigProvider; import com.antheminc.oss.nimbus.domain.model.config.builder.internal.DetourExecutionConfigProvider; @@ -145,6 +146,11 @@ public DetourExecutionConfigProvider detourExecutionConfigProvider(BeanResolverS return new DetourExecutionConfigProvider(); } + @Bean + public ConfigVariableExecutionConfigProvider configVariableExecutionConfigProvider() { + return new ConfigVariableExecutionConfigProvider(); + } + @Bean(name="default.annotationConfigBuilder") public AnnotationConfigHandler annotationConfigHandler(BeanResolverStrategy beanResolver, PropertyResolver propertyResolver) { Map, AnnotationAttributeHandler> attributeHandlers = new HashMap<>(); diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreExecutorConfig.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreExecutorConfig.java index 27a517b74..0e8e90f43 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreExecutorConfig.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreExecutorConfig.java @@ -24,10 +24,10 @@ import com.antheminc.oss.nimbus.domain.cmd.exec.CommandExecutor; import com.antheminc.oss.nimbus.domain.cmd.exec.CommandPathVariableResolver; import com.antheminc.oss.nimbus.domain.cmd.exec.CommandTransactionInterceptor; -import com.antheminc.oss.nimbus.domain.cmd.exec.DefaultConfigPlaceholderResolver; +import com.antheminc.oss.nimbus.domain.cmd.exec.ConfigVariableResolver; +import com.antheminc.oss.nimbus.domain.cmd.exec.DefaultConfigVariableResolver; import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContextLoader; import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContextPathVariableResolver; -import com.antheminc.oss.nimbus.domain.cmd.exec.ConfigPlaceholderResolver; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.DefaultActionExecutorConfig; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.DefaultActionExecutorDelete; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.DefaultActionExecutorGet; @@ -80,8 +80,8 @@ public ExecutionContextPathVariableResolver defaultExecutionContextPathVariableR } @Bean - public ConfigPlaceholderResolver paramPathPlaceholderResolver(ReservedKeywordRegistry reservedKeywordRegistry) { - return new DefaultConfigPlaceholderResolver(reservedKeywordRegistry); + public ConfigVariableResolver configVariableResolver(ReservedKeywordRegistry reservedKeywordRegistry) { + return new DefaultConfigVariableResolver(reservedKeywordRegistry); } @Bean(name="default.ExecutionContextLoader", destroyMethod="clear") diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultProcessConfig.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultProcessConfig.java index 2b0dcb9e3..8cd83dd05 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultProcessConfig.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultProcessConfig.java @@ -30,9 +30,11 @@ import com.antheminc.oss.nimbus.domain.bpm.activiti.DefaultMongoProcessRepository; import com.antheminc.oss.nimbus.domain.cmd.exec.FunctionHandler; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.DefaultParamFunctionHandler; +import com.antheminc.oss.nimbus.domain.cmd.exec.internal.ReservedKeywordRegistry; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.nav.DefaultActionNewInitEntityFunctionHandler; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.nav.PageIdEchoNavHandler; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.process.AddFunctionHandler; +import com.antheminc.oss.nimbus.domain.cmd.exec.internal.process.ConfigVariableFunctionHandler; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.process.EvalFunctionHandler; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.process.SetByRuleFunctionalHandler; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.process.SetFunctionHandler; @@ -139,4 +141,8 @@ public EvalFunctionHandler evalFunctionHandler(ExpressionManager expression return new EvalFunctionHandler(expressionManager); } + @Bean(name="default._process$execute?fn=_setConfigVariable") + public FunctionHandler configVariableFunctionHandler(ReservedKeywordRegistry reservedKeywordRegistry){ + return new ConfigVariableFunctionHandler<>(reservedKeywordRegistry); + } } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/CommandExecutorGateway.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/CommandExecutorGateway.java index a470fc621..f1907ff6a 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/CommandExecutorGateway.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/CommandExecutorGateway.java @@ -15,12 +15,12 @@ */ package com.antheminc.oss.nimbus.domain.cmd.exec; +import java.lang.annotation.Annotation; import java.util.List; import com.antheminc.oss.nimbus.domain.cmd.Command; import com.antheminc.oss.nimbus.domain.cmd.CommandMessage; import com.antheminc.oss.nimbus.domain.cmd.exec.CommandExecution.MultiOutput; -import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig; import com.antheminc.oss.nimbus.domain.model.state.EntityState.Param; /** @@ -34,6 +34,6 @@ public interface CommandExecutorGateway { MultiOutput execute(CommandMessage cmdMsg); - List executeConfig(ExecutionContext eCtx, Param cmdParam, ExecutionConfig exectionConfig); + List executeConfig(ExecutionContext eCtx, Param cmdParam, List execConfigs); } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ConfigPlaceholderResolver.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ConfigVariableResolver.java similarity index 76% rename from nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ConfigPlaceholderResolver.java rename to nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ConfigVariableResolver.java index f48161f14..7586428ad 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ConfigPlaceholderResolver.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ConfigVariableResolver.java @@ -15,23 +15,21 @@ */ package com.antheminc.oss.nimbus.domain.cmd.exec; -import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig.Context; - /** * @author Tony Lopez * @since 1.3 * */ -public interface ConfigPlaceholderResolver { +public interface ConfigVariableResolver { /** - *

Resolve any config placeholders that are present in the the provided + *

Resolve any config variables that are present in the the provided * path and return the resolved result. * @param context the context object from which to retrieve config - * placeholders + * variables * @param pathToResolve the path to resolve against * @return the resolved path */ - String resolve(Context context, String pathToResolve); + String resolve(ExecutionContext eCtx, String pathToResolve); } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigPlaceholderResolver.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigVariableResolver.java similarity index 75% rename from nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigPlaceholderResolver.java rename to nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigVariableResolver.java index 6874ecd36..8f487a7ef 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigPlaceholderResolver.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigVariableResolver.java @@ -23,7 +23,6 @@ import org.apache.commons.lang3.StringUtils; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.ReservedKeywordRegistry; -import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig.Context; import lombok.RequiredArgsConstructor; @@ -33,17 +32,17 @@ * */ @RequiredArgsConstructor -public class DefaultConfigPlaceholderResolver implements ConfigPlaceholderResolver { +public class DefaultConfigVariableResolver implements ConfigVariableResolver { private final ReservedKeywordRegistry reservedKeywordRegistry; @Override - public String resolve(Context context, String pathToResolve) { + public String resolve(ExecutionContext eCtx, String pathToResolve) { Map entries = ParamPathExpressionParser.parse(pathToResolve); // remove reserved placeholders -- those will be handled by another // resolver - entries = entries.entrySet().stream().filter(x -> !this.reservedKeywordRegistry.exists(x.getValue())) + entries = entries.entrySet().stream().filter(x -> !this.reservedKeywordRegistry.exists(ParamPathExpressionParser.stripPrefixSuffix(x.getValue()))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); if (MapUtils.isEmpty(entries)) { @@ -52,9 +51,10 @@ public String resolve(Context context, String pathToResolve) { String out = pathToResolve; for (Entry entry : entries.entrySet()) { - String placeholderName = ParamPathExpressionParser.stripPrefixSuffix(entry.getValue()); - String placeholderValue = context.getPlaceholderMap().get(placeholderName); - out = StringUtils.replace(out, entry.getValue(), placeholderValue, 1); + String variableName = ParamPathExpressionParser.stripPrefixSuffix(entry.getValue()); + Object variableValue = eCtx.getVariable(variableName); + String sVariableValue = variableValue != null ? variableValue.toString() : null; + out = StringUtils.replace(out, entry.getValue(), sVariableValue, 1); } return out; diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ExecutionContext.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ExecutionContext.java index 0e8cce200..2f2b2e90d 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ExecutionContext.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ExecutionContext.java @@ -16,10 +16,13 @@ package com.antheminc.oss.nimbus.domain.cmd.exec; import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; -import org.apache.commons.lang3.StringUtils; import com.antheminc.oss.nimbus.domain.cmd.Command; import com.antheminc.oss.nimbus.domain.cmd.CommandMessage; @@ -40,17 +43,17 @@ public class ExecutionContext implements Serializable { private static final long serialVersionUID = 1L; private final CommandMessage commandMessage; - + private final Map resolvedVariableMap = new HashMap<>(); private QuadModel quadModel; public ExecutionContext(Command command) { this(new CommandMessage(command, null)); } - + public ExecutionContext(CommandMessage commandMessage) { this.commandMessage = commandMessage; } - + public ExecutionContext(CommandMessage commandMessage, QuadModel quadModel) { this(commandMessage); setQuadModel(quadModel); @@ -60,6 +63,24 @@ public String getId() { return getCommandMessage().getCommand().getRootDomainUri(); } + /** + *

Get the map of currently resolved variables. + * @return the map of current resolved variables. + */ + public Map getResolvedVariableMap() { + return Collections.unmodifiableMap(this.resolvedVariableMap); + } + + /** + *

Get the resolved variable value for a given variable name. + * @param name the name of the variable to retrieve + * @return the previous value associated with {@code name}, or + * {@code null} if there was no mapping for {@code name}. + */ + public Object getVariable(String name) { + return this.resolvedVariableMap.get(name); + } + public boolean equalsId(Command cmd) { return StringUtils.equals(getId(), cmd.getRootDomainUri()); } @@ -76,7 +97,6 @@ public

P findStateByPath(String path) { return getRootModel().findStateByPath(path); } - @Override public boolean equals(Object other) { if(other==null) @@ -99,6 +119,17 @@ public int hashCode() { return builder.hashCode(); } + /** + *

Set a resolve variable value into the resolved variable cache. + * @param name the name to store the variable under + * @param value the value to store + * @return the previous value associated with {@code name}, or + * {@code null} if there was no mapping for {@code name}. + */ + public Object setVariable(String name, Object value) { + return this.resolvedVariableMap.put(name, value); + } + @Override public String toString() { return getId(); diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandExecutorGateway.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandExecutorGateway.java index 97f2fed3f..29393db02 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandExecutorGateway.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandExecutorGateway.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,9 @@ package com.antheminc.oss.nimbus.domain.cmd.exec.internal; import java.lang.annotation.Annotation; +import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -50,19 +52,19 @@ import com.antheminc.oss.nimbus.domain.cmd.exec.CommandExecutor; import com.antheminc.oss.nimbus.domain.cmd.exec.CommandExecutorGateway; import com.antheminc.oss.nimbus.domain.cmd.exec.CommandPathVariableResolver; +import com.antheminc.oss.nimbus.domain.cmd.exec.ConfigVariableResolver; import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContext; import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContextLoader; import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContextPathVariableResolver; import com.antheminc.oss.nimbus.domain.cmd.exec.ParamPathExpressionParser; -import com.antheminc.oss.nimbus.domain.cmd.exec.ConfigPlaceholderResolver; import com.antheminc.oss.nimbus.domain.config.builder.DomainConfigBuilder; import com.antheminc.oss.nimbus.domain.defn.Constants; import com.antheminc.oss.nimbus.domain.defn.Execution.Config; +import com.antheminc.oss.nimbus.domain.defn.Execution.Let; import com.antheminc.oss.nimbus.domain.defn.builder.internal.ExecutionConfigBuilder; import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig; import com.antheminc.oss.nimbus.domain.model.config.ModelConfig; import com.antheminc.oss.nimbus.domain.model.config.builder.ExecutionConfigProvider; -import com.antheminc.oss.nimbus.domain.model.config.internal.DefaultExecutionConfig; import com.antheminc.oss.nimbus.domain.model.state.EntityState.ExecutionModel; import com.antheminc.oss.nimbus.domain.model.state.EntityState.Param; import com.antheminc.oss.nimbus.domain.model.state.ExecutionTxnContext; @@ -95,7 +97,7 @@ public class DefaultCommandExecutorGateway extends BaseCommandExecutorStrategies private CommandPathVariableResolver pathVariableResolver; - private ConfigPlaceholderResolver configPlaceholderResolver; + private ConfigVariableResolver configVariableResolver; private ExecutionContextPathVariableResolver eCtxPathVariableResolver; @@ -123,7 +125,7 @@ public void initDependencies() { this.domainConfigBuilder = getBeanResolver().get(DomainConfigBuilder.class); this.cmdHandler = getBeanResolver().get(ChangeLogCommandEventHandler.class); this.expressionEvaluator = getBeanResolver().get(ExpressionEvaluator.class); - this.configPlaceholderResolver = getBeanResolver().get(ConfigPlaceholderResolver.class); + this.configVariableResolver = getBeanResolver().get(ConfigVariableResolver.class); } @@ -221,7 +223,7 @@ protected MultiOutput executeInternal(ExecutionContext eCtx, CommandMessage cmdM ExecutionConfig executionConfig = cmdParam != null ? cmdParam.getConfig().getExecutionConfig() : null; if(cmdMsg.getCommand().getAction() == Action._get && executionConfig != null && CollectionUtils.isNotEmpty(executionConfig.get())) { - List execConfigOutputs = executeConfig(eCtx, cmdParam, executionConfig); + List execConfigOutputs = executeConfig(eCtx, cmdParam, executionConfig.get()); execConfigOutputs.stream().forEach(mOut->addMultiOutput(mOutput, mOut)); } else {// otherwise, execute self @@ -239,28 +241,27 @@ protected void validateCommand(CommandMessage cmdMsg) { cmdMsg.getCommand().validate(); } - @SuppressWarnings("unchecked") @Override - public List executeConfig(ExecutionContext eCtx, Param cmdParam, ExecutionConfig executionConfig) { + public List executeConfig(ExecutionContext eCtx, Param cmdParam, List execConfigs) { final CommandMessage cmdMsg = eCtx.getCommandMessage(); boolean isPayloadUsed = false; final List configExecOutputs = new ArrayList<>(); - executionConfig.get().stream().forEach(ec-> { + execConfigs.stream().forEach(ec-> { final ExecutionConfigProvider execConfigProvider = getBeanResolver().get(ExecutionConfigProvider.class, ec.annotationType()); - Config config = execConfigProvider.getMain(ec); + Config config = execConfigProvider.getMain(ec, eCtx); try { boolean evalWhen = getExpressionEvaluator().getValue(config.when(), cmdParam, Boolean.class); if(!evalWhen) return; if(StringUtils.isNotBlank(config.col())) { - buildAndExecuteColExecConfig(eCtx, cmdParam, executionConfig, config); + buildAndExecuteColExecConfig(eCtx, cmdParam, config); } else { String resolvedURL0 = eCtx.getCommandMessage().getCommand().getRelativeUri(config.url()); String resolvedURL1 = getECtxPathVariableResolver().resolve(eCtx, cmdParam, resolvedURL0); - String resolvedURL2 = getConfigPlaceholderResolver().resolve(executionConfig.getContext(), resolvedURL1); + String resolvedURL2 = getConfigVariableResolver().resolve(eCtx, resolvedURL1); String resolvedURL3 = getPathVariableResolver().resolve(cmdParam, resolvedURL2); Command configExecCmd = CommandBuilder.withUri(resolvedURL3).getCommand(); @@ -268,20 +269,22 @@ public List executeConfig(ExecutionContext eCtx, Param cmdParam, // execute & add output to mOutput MultiOutput configOutput = executeConfig(eCtx.getCommandMessage().getCommand(), configCmdMsg); - configExecOutputs.add(configOutput); + if (Let.class.isAssignableFrom(ec.annotationType())) { + SimpleEntry entry = (SimpleEntry) configOutput.getSingleResult(); + eCtx.setVariable(entry.getKey(), entry.getValue()); + } else { + configExecOutputs.add(configOutput); + } } } catch(Exception ex) { - Config exceptionConfig = execConfigProvider.getException(ec); + Config exceptionConfig = execConfigProvider.getException(ec, eCtx); if(exceptionConfig == null) // if exception config not configured, default to throw ex throw ex; logit.error(() -> "Failed to execute main config "+ config +" on param "+ cmdParam +" , executing the onException config", ex); - - DefaultExecutionConfig exceptionExecutionConfig = new DefaultExecutionConfig(); - exceptionExecutionConfig.add(exceptionConfig); - exceptionExecutionConfig.setContext(executionConfig.getContext()); - executeConfig(eCtx, cmdParam, exceptionExecutionConfig); + + executeConfig(eCtx, cmdParam, Arrays.asList(exceptionConfig)); } }); return configExecOutputs; @@ -322,7 +325,7 @@ private MultiOutput executeConfig(Command inputCmd, CommandMessage configCmdMsg) } } - private void buildAndExecuteColExecConfig(ExecutionContext eCtx, Param cmdParam, ExecutionConfig executionConfig, Config ec) { + private void buildAndExecuteColExecConfig(ExecutionContext eCtx, Param cmdParam, Config ec) { List colExecConfigs = new ArrayList<>(); String colPath = ParamPathExpressionParser.stripPrefixSuffix(ec.col()); @@ -333,7 +336,7 @@ private void buildAndExecuteColExecConfig(ExecutionContext eCtx, Param cmdPar if(p.isCollection()) { for(int i=0; i < p.findIfCollection().size(); i++) { - String resolvedURL1 = getConfigPlaceholderResolver().resolve(executionConfig.getContext(), ec.url()); + String resolvedURL1 = getConfigVariableResolver().resolve(eCtx, ec.url()); String resolvedURL2 = StringUtils.replace(resolvedURL1,Constants.MARKER_COL_PARAM.code,colPath+Constants.SEPARATOR_URI.code+i); colExecConfigs.add(ExecutionConfigBuilder.buildExecConfig(resolvedURL2, ec.order())); } @@ -346,15 +349,13 @@ else if(p.getConfig().getType().isArray()) { int size = ArrayUtils.getLength(arrayParamState); for(int i=0; i < size; i++) { - String resolvedURL1 = getConfigPlaceholderResolver().resolve(executionConfig.getContext(), ec.url()); + String resolvedURL1 = getConfigVariableResolver().resolve(eCtx, ec.url()); String resolvedURL2 = StringUtils.replace(resolvedURL1, Constants.MARKER_COL_PARAM_EXPR.code, String.valueOf(arrayParamState[i])); colExecConfigs.add(ExecutionConfigBuilder.buildExecConfig(resolvedURL2, ec.order())); } } - - DefaultExecutionConfig colExecutionConfig = new DefaultExecutionConfig(); - colExecConfigs.forEach(colExecutionConfig::add); - executeConfig(eCtx, cmdParam, colExecutionConfig); + + executeConfig(eCtx, cmdParam, colExecConfigs); } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandPathVariableResolver.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandPathVariableResolver.java index 909e0dd9e..1061fee24 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandPathVariableResolver.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandPathVariableResolver.java @@ -63,8 +63,8 @@ public class DefaultCommandPathVariableResolver implements CommandPathVariableRe public class ElemIdResolver extends Resolver { - public ElemIdResolver(String keyword) { - super(keyword); + public ElemIdResolver() { + super(Constants.MARKER_ELEM_ID.code); } @Override @@ -86,42 +86,53 @@ protected String mapColElem(Param param, String pathToResolve) { } } - public class EnvResolver extends Resolver { + public class PassthroughResolver extends Resolver { - public EnvResolver(String keyword) { + public PassthroughResolver(String keyword) { super(keyword); } + + @Override + String resolve(Param param, String pathToResolve) { + return Constants.MARKER_PLATFROM_EXPR_PREFIX.code + pathToResolve + Constants.MARKER_PLATFROM_EXPR_SUFFIX.code; + } + } + + public class EnvResolver extends Resolver { + + public EnvResolver() { + super(Constants.MARKER_ENV.code); + } @Override public String resolve(Param param, String pathToResolve) { String property = pathToResolve.replace(Constants.MARKER_ENV.code+".", ""); return environment.getProperty(property); } - } + public class PlatformMarkerResolver extends Resolver { - public PlatformMarkerResolver(String keyword) { - super(keyword); + public PlatformMarkerResolver() { + super(Constants.SEGMENT_PLATFORM_MARKER.code); } @Override public String resolve(Param param, String pathToResolve) { return String.valueOf(mapCrossDomain(param, pathToResolve)); } - } + public class RefIdResolver extends Resolver { - public RefIdResolver(String keyword) { - super(keyword); + public RefIdResolver() { + super(Constants.MARKER_REF_ID.code); } @Override public String resolve(Param param, String pathToResolve) { return String.valueOf(param.getRootExecution().getRootCommand().getRefId(Type.DomainAlias)); } - } @RequiredArgsConstructor @@ -139,8 +150,8 @@ protected boolean shouldApply(String pathToResolve) { public class SelfResolver extends Resolver { - public SelfResolver(String keyword) { - super(keyword); + public SelfResolver() { + super(Constants.MARKER_SESSION_SELF.code); } @Override @@ -154,20 +165,20 @@ public String resolve(Param param, String pathToResolve) { return param.getRootExecution().getRootCommand().getElementSafely(Type.ClientAlias).getAlias(); } - } + public class ThisResolver extends Resolver { - public ThisResolver(String keyword) { - super(keyword); + public ThisResolver() { + super(Constants.MARKER_COMMAND_PARAM_CURRENT_SELF.code); } @Override public String resolve(Param param, String pathToResolve) { return StringUtils.removeStart(param.getPath(), param.getRootDomain().getPath()); } - } + private static final String NULL_STRING = "null"; private static final String NULL_STRING_REGEX = "\\s*\"null\"\\s*"; private static final Pattern NULL_STRING_PATTERN = Pattern.compile(NULL_STRING_REGEX); @@ -194,12 +205,13 @@ public DefaultCommandPathVariableResolver(BeanResolverStrategy beanResolver, Pro } public void registerDefaultResolvers() { - registerResolver(new SelfResolver(Constants.MARKER_SESSION_SELF.code)); - registerResolver(new EnvResolver(Constants.MARKER_ENV.code)); - registerResolver(new ThisResolver(Constants.MARKER_COMMAND_PARAM_CURRENT_SELF.code)); - registerResolver(new RefIdResolver(Constants.MARKER_REF_ID.code)); - registerResolver(new ElemIdResolver(Constants.MARKER_ELEM_ID.code)); - registerResolver(new PlatformMarkerResolver(Constants.SEGMENT_PLATFORM_MARKER.code)); + registerResolver(new SelfResolver()); + registerResolver(new EnvResolver()); + registerResolver(new ThisResolver()); + registerResolver(new RefIdResolver()); + registerResolver(new ElemIdResolver()); + registerResolver(new PlatformMarkerResolver()); + registerResolver(new PassthroughResolver(Constants.MARKER_COL_PARAM.code)); } public void registerResolver(Resolver resolver) { diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/process/ConfigVariableFunctionHandler.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/process/ConfigVariableFunctionHandler.java new file mode 100644 index 000000000..80fb23968 --- /dev/null +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/process/ConfigVariableFunctionHandler.java @@ -0,0 +1,103 @@ +/** + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.antheminc.oss.nimbus.domain.cmd.exec.internal.process; + +import java.util.AbstractMap.SimpleEntry; + +import org.springframework.util.StringUtils; + +import com.antheminc.oss.nimbus.InvalidConfigException; +import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContext; +import com.antheminc.oss.nimbus.domain.cmd.exec.FunctionHandler; +import com.antheminc.oss.nimbus.domain.cmd.exec.internal.ReservedKeywordRegistry; +import com.antheminc.oss.nimbus.domain.model.state.EntityState.Param; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * @author Tony Lopez + * + */ +@RequiredArgsConstructor +@Getter +public class ConfigVariableFunctionHandler implements FunctionHandler { + + public static final String ARG_CONFIG = "config"; + public static final String ARG_NAME = "name"; + public static final String ARG_SPEL = "spel"; + public static final String ARG_VALUE = "value"; + + private final ReservedKeywordRegistry reservedKeywordRegistry; + + @Override + public Object execute(ExecutionContext eCtx, Param actionParameter) { + validate(eCtx, actionParameter); + String variableName = eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_NAME); + Object variableValue = determineVariableValue(eCtx, actionParameter); + return new SimpleEntry(variableName, variableValue); + } + + private Object determineVariableValue(ExecutionContext eCtx, Param actionParameter) { + String instruction = eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_VALUE); + if (!StringUtils.isEmpty(instruction)) { + return instruction; + } + + instruction = eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_SPEL); + if (!StringUtils.isEmpty(instruction)) { + // TODO + } + + instruction = eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_CONFIG); + if (!StringUtils.isEmpty(instruction)) { + // TODO + } + + return null; + } + + /** + *

Validate that the necessary information has been provided. + * + * @param eCtx the execution context + * @param actionParameter the action param + */ + private void validate(ExecutionContext eCtx, Param actionParameter) { + if (null == actionParameter) { + throw new InvalidConfigException("The value provided for actionParameter must be non-null."); + } + + String name = eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_NAME); + if (StringUtils.isEmpty(name)) { + throw new InvalidConfigException("The argument \"" + ARG_NAME + "\" is required."); + } + + if (getReservedKeywordRegistry().exists(name)) { + throw new InvalidConfigException("The placeholder \"" + name + + "\" is a reserved keyword. Replacing reserved placeholders is not allowed."); + } + + if (StringUtils.isEmpty(eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_VALUE))) { + if (StringUtils.isEmpty(eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_SPEL))) { + if (StringUtils.isEmpty(eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_CONFIG))) { + throw new InvalidConfigException("At least one value is required for either: " + ARG_VALUE + ", " + + ARG_SPEL + ", or " + ARG_CONFIG + "."); + } + } + } + } +} diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/Execution.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/Execution.java index d0e5d367f..6f92703f0 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/Execution.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/Execution.java @@ -28,6 +28,7 @@ import com.antheminc.oss.nimbus.domain.Event; import com.antheminc.oss.nimbus.domain.defn.Executions.Configs; import com.antheminc.oss.nimbus.domain.defn.Executions.DetourConfigs; +import com.antheminc.oss.nimbus.domain.defn.Executions.LetMany; /** * @author Soham Chakravarti @@ -93,55 +94,67 @@ } /** - * @author Rakesh Patel - * - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.FIELD }) - @Repeatable(DetourConfigs.class) - @Execution - public @interface DetourConfig { - Config main(); - - Config onException() default @Config(url = ""); - - int order() default Event.DEFAULT_ORDER_NUMBER; - } - - /** - *

Annotation which indicates that a command template variable should - * be bound to a provided value. Supported for {@link Config} annotated - * fields during the execution step. + *

Annotation which indicates that a command template variable should be + * bound to a provided value. Supported for {@link Config} annotated fields + * during the execution step. * *

The command template variable must not match any reserved template * variables. * * @author Tony Lopez * @since 1.3 - * @see com.antheminc.oss.nimbus.domain.cmd.exec.ConfigPlaceholderResolver */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD }) - @Repeatable(ConfigPlaceholders.class) + @Repeatable(LetMany.class) @Execution - public @interface ConfigPlaceholder { + public @interface Let { /** - *

The name of the placeholder that should be used as an identifier to - * resolve in a config url. + *

A {@link Config} execution to resolve and use in place of + * {@link #name()} when an execution using this variable is resolved. + */ + Config[] config() default {}; + + /** + *

The name of the placeholder that should be used as an identifier + * to resolve in a config url. */ String name(); /** - *

The value to use in place of {@link #name()} when this config placeholder is resolved. + *

The order of execution this annotation should be executed in, with + * respect to other conditional annotations that are also decorating + * this param. + */ + int order() default Event.DEFAULT_ORDER_NUMBER; + + /** + *

A SpEL expression to resolve and use in place of {@link #name()} + * when an execution using this variable is resolved. */ - String value(); + String spel() default ""; + + /** + *

The static value to use in place of {@link #name()} when an + * execution using this variable is resolved. + */ + String value() default ""; } + /** + * @author Rakesh Patel + * + */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD }) + @Repeatable(DetourConfigs.class) @Execution - public @interface ConfigPlaceholders { - ConfigPlaceholder[] value(); + public @interface DetourConfig { + Config main(); + + Config onException() default @Config(url = ""); + + int order() default Event.DEFAULT_ORDER_NUMBER; } } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/Executions.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/Executions.java index 50404f539..f754cee00 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/Executions.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/Executions.java @@ -22,6 +22,7 @@ import com.antheminc.oss.nimbus.domain.defn.Execution.Config; import com.antheminc.oss.nimbus.domain.defn.Execution.DetourConfig; +import com.antheminc.oss.nimbus.domain.defn.Execution.Let; /** * @author Soham Chakravarti @@ -45,4 +46,10 @@ public class Executions { } + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.FIELD}) + @Execution + public @interface LetMany { + Let[] value(); + } } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/ConfigPlaceholderValidator.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/ConfigVariableValidator.java similarity index 86% rename from nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/ConfigPlaceholderValidator.java rename to nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/ConfigVariableValidator.java index faf135cc9..d0fc3894a 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/ConfigPlaceholderValidator.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/ConfigVariableValidator.java @@ -17,7 +17,7 @@ import com.antheminc.oss.nimbus.InvalidConfigException; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.ReservedKeywordRegistry; -import com.antheminc.oss.nimbus.domain.defn.Execution.ConfigPlaceholder; +import com.antheminc.oss.nimbus.domain.defn.Execution.Let; import lombok.RequiredArgsConstructor; @@ -28,16 +28,16 @@ * @since 1.3 */ @RequiredArgsConstructor -public class ConfigPlaceholderValidator { +public class ConfigVariableValidator { private final ReservedKeywordRegistry reservedKeywordRegistry; public boolean supports(Class clazz) { - return ConfigPlaceholder.class.isAssignableFrom(clazz); + return Let.class.isAssignableFrom(clazz); } public void validate(Object target) { - ConfigPlaceholder obj = (ConfigPlaceholder) target; + Let obj = (Let) target; if (this.reservedKeywordRegistry.exists(obj.name())) { throw new InvalidConfigException("The placeholder \"" + obj.name() + "\" is a reserved keyword. Replacing reserved placeholders is not allowed."); diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/ExecutionConfig.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/ExecutionConfig.java index c0bae6538..05da34734 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/ExecutionConfig.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/ExecutionConfig.java @@ -4,13 +4,7 @@ package com.antheminc.oss.nimbus.domain.model.config; import java.lang.annotation.Annotation; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.stream.Stream; - -import com.antheminc.oss.nimbus.domain.defn.Execution.ConfigPlaceholder; /** * @author Rakesh Patel @@ -19,59 +13,4 @@ public interface ExecutionConfig { public List get(); - - /** - *

Retrieve the context details for this execution config instance. - * @return the context - */ - public Context getContext(); - - /** - *

A context object containing details related to an execution config - * instance. Data stored here should be used to support execution - * instructions. - * @author Tony Lopez - * @since 1.3 - */ - public static class Context { - private final Map placeholders; - - public Context() { - this.placeholders = new HashMap<>(); - } - - /** - *

Add a placeholder to the context. - * @param placeholder the placeholder to add - */ - public void addPlaceholder(ConfigPlaceholder placeholder) { - this.placeholders.put(placeholder.name(), placeholder.value()); - } - - /** - *

Add placeholders to the context. - * @param placeholders the placeholder to add - */ - public void addPlaceholders(ConfigPlaceholder[] placeholders) { - Stream.of(placeholders).forEach(this::addPlaceholder); - } - - /** - *

Retrieve a placeholder value by it's name. - * @param placeholderName the name of the placeholder for which to - * retrieve - */ - public String getPlaceholder(String placeholderName) { - return this.placeholders.get(placeholderName); - } - - /** - *

Get the map representation of the placeholders, with the key being - * the placeholder name and the value being the value to resolve the key - * to. - */ - public Map getPlaceholderMap() { - return Collections.unmodifiableMap(this.placeholders); - } - } } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/ExecutionConfigProvider.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/ExecutionConfigProvider.java index 1ec22a204..116f0becb 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/ExecutionConfigProvider.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/ExecutionConfigProvider.java @@ -1,20 +1,54 @@ /** - * + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.antheminc.oss.nimbus.domain.model.config.builder; import java.lang.annotation.Annotation; +import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContext; import com.antheminc.oss.nimbus.domain.defn.Execution.Config; /** + *

An interpreter which takes a given annotation and maps {@link Execution} + * typed annotations into their equivalent {@link Config} annotations. See + * implementations for how specific annotations are handled. + * * @author Rakesh Patel + * @author Tony Lopez * */ public interface ExecutionConfigProvider { - - public Config getMain(A configAnnotation); - - public Config getException(A configAnnotation); + + /** + *

Get the main {@link Config} related statements to execute from + * {@code configAnnotation}. + * @param configAnnotation the annotation from which to retrieve + * {@link Config} statements from + * @param eCtx the execution context + * @return the final {@link Config} statements + */ + public Config getMain(A configAnnotation, ExecutionContext eCtx); + + /** + *

Get the exception {@link Config} related statements to execute from + * {@code configAnnotation}. + * @param configAnnotation the annotation from which to retrieve + * {@link Config} statements from + * @param eCtx the execution context + * @return the final {@link Config} statements + */ + public Config getException(A configAnnotation, ExecutionContext eCtx); } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ConfigVariableExecutionConfigProvider.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ConfigVariableExecutionConfigProvider.java new file mode 100644 index 000000000..64eeb64bf --- /dev/null +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ConfigVariableExecutionConfigProvider.java @@ -0,0 +1,46 @@ +/** + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.antheminc.oss.nimbus.domain.model.config.builder.internal; + +import com.antheminc.oss.nimbus.domain.cmd.Action; +import com.antheminc.oss.nimbus.domain.cmd.CommandMessage; +import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContext; +import com.antheminc.oss.nimbus.domain.defn.Execution.Config; +import com.antheminc.oss.nimbus.domain.defn.Execution.Let; +import com.antheminc.oss.nimbus.domain.defn.builder.internal.ExecutionConfigBuilder; +import com.antheminc.oss.nimbus.domain.model.config.builder.ExecutionConfigProvider; + +/** + * @author Tony Lopez + * + */ +public class ConfigVariableExecutionConfigProvider implements ExecutionConfigProvider { + + @Override + public Config getMain(Let configAnnotation, ExecutionContext eCtx) { + final CommandMessage cmdMsg = eCtx.getCommandMessage(); + // TODO review and clean up. + final String valueString = "value=" + configAnnotation.value(); + String uri = "/p" + cmdMsg.getCommand().getAbsoluteDomainUri() + "/" + Action._process + "?fn=_setConfigVariable&name=" + configAnnotation.name() + "&" + valueString; + return ExecutionConfigBuilder.buildExecConfig(uri); + } + + @Override + public Config getException(Let configAnnotation, ExecutionContext eCtx) { + return null; + } + +} diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/DefaultExecutionConfigProvider.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/DefaultExecutionConfigProvider.java index 0e42ff244..6d4c193e4 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/DefaultExecutionConfigProvider.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/DefaultExecutionConfigProvider.java @@ -1,8 +1,21 @@ /** - * + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.antheminc.oss.nimbus.domain.model.config.builder.internal; +import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContext; import com.antheminc.oss.nimbus.domain.defn.Execution.Config; import com.antheminc.oss.nimbus.domain.model.config.builder.ExecutionConfigProvider; @@ -13,12 +26,12 @@ public class DefaultExecutionConfigProvider implements ExecutionConfigProvider { @Override - public Config getMain(Config configAnnotation) { + public Config getMain(Config configAnnotation, ExecutionContext eCtx) { return configAnnotation; } @Override - public Config getException(Config configAnnotation) { + public Config getException(Config configAnnotation, ExecutionContext eCtx) { return null; } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/DetourExecutionConfigProvider.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/DetourExecutionConfigProvider.java index eeaa164b6..c6a486fa8 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/DetourExecutionConfigProvider.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/DetourExecutionConfigProvider.java @@ -1,10 +1,23 @@ /** - * + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.antheminc.oss.nimbus.domain.model.config.builder.internal; import org.apache.commons.lang3.StringUtils; +import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContext; import com.antheminc.oss.nimbus.domain.defn.Execution.Config; import com.antheminc.oss.nimbus.domain.defn.Execution.DetourConfig; import com.antheminc.oss.nimbus.domain.model.config.builder.ExecutionConfigProvider; @@ -16,12 +29,12 @@ public class DetourExecutionConfigProvider implements ExecutionConfigProvider { @Override - public Config getMain(DetourConfig configAnnotation) { + public Config getMain(DetourConfig configAnnotation, ExecutionContext eCtx) { return configAnnotation.main(); } @Override - public Config getException(DetourConfig configAnnotation) { + public Config getException(DetourConfig configAnnotation, ExecutionContext eCtx) { return configAnnotation.onException() != null && StringUtils.isNotBlank(configAnnotation.onException().url()) ? configAnnotation.onException() : null; diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ExecutionConfigFactory.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ExecutionConfigFactory.java index 6da791c15..bc3e1104f 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ExecutionConfigFactory.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ExecutionConfigFactory.java @@ -1,5 +1,17 @@ /** - * + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.antheminc.oss.nimbus.domain.model.config.builder.internal; @@ -7,43 +19,49 @@ import com.antheminc.oss.nimbus.domain.cmd.exec.internal.ReservedKeywordRegistry; import com.antheminc.oss.nimbus.domain.defn.Execution.Config; -import com.antheminc.oss.nimbus.domain.defn.Execution.ConfigPlaceholder; import com.antheminc.oss.nimbus.domain.defn.Execution.DetourConfig; -import com.antheminc.oss.nimbus.domain.defn.validaton.ConfigPlaceholderValidator; +import com.antheminc.oss.nimbus.domain.defn.Execution.Let; +import com.antheminc.oss.nimbus.domain.defn.validaton.ConfigVariableValidator; import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig; -import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig.Context; import com.antheminc.oss.nimbus.domain.model.config.internal.DefaultExecutionConfig; /** * @author Rakesh Patel + * @author Tony Lopez * */ public class ExecutionConfigFactory { - - private final ConfigPlaceholderValidator placeholderValidator; - + + private final ConfigVariableValidator configVariableValidator; + public ExecutionConfigFactory(ReservedKeywordRegistry reservedKeywordRegistry) { - this.placeholderValidator = new ConfigPlaceholderValidator(reservedKeywordRegistry); + this.configVariableValidator = new ConfigVariableValidator(reservedKeywordRegistry); } - + public ExecutionConfig build(AnnotatedElement aElem) { - final Config arr[] = aElem.getAnnotationsByType(Config.class); - final DetourConfig arr2[] = aElem.getAnnotationsByType(DetourConfig.class); - DefaultExecutionConfig executionConfig = new DefaultExecutionConfig(); - executionConfig.addAll(arr); - executionConfig.addAll(arr2); + buildConfigs(executionConfig, aElem); + buildDetourConfigs(executionConfig, aElem); + buildConfigVariables(executionConfig, aElem); executionConfig.sort(); - - Context context = new Context(); - final ConfigPlaceholder[] placeholders = aElem.getAnnotationsByType(ConfigPlaceholder.class); - for(ConfigPlaceholder placeholder : placeholders) { - placeholderValidator.validate(placeholder); - context.addPlaceholder(placeholder); - } - executionConfig.setContext(context); - return executionConfig; - + } + + protected void buildConfigs(DefaultExecutionConfig exectionConfig, AnnotatedElement aElem) { + final Config arr[] = aElem.getAnnotationsByType(Config.class); + exectionConfig.addAll(arr); + } + + protected void buildConfigVariables(DefaultExecutionConfig executionConfig, AnnotatedElement aElem) { + final Let[] variables = aElem.getAnnotationsByType(Let.class); + for (Let variable : variables) { + configVariableValidator.validate(variable); + executionConfig.add(variable); + } + } + + protected void buildDetourConfigs(DefaultExecutionConfig exectionConfig, AnnotatedElement aElem) { + final DetourConfig arr[] = aElem.getAnnotationsByType(DetourConfig.class); + exectionConfig.addAll(arr); } } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/internal/DefaultExecutionConfig.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/internal/DefaultExecutionConfig.java index 13bd9464e..af827e766 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/internal/DefaultExecutionConfig.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/internal/DefaultExecutionConfig.java @@ -1,5 +1,17 @@ /** - * + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.antheminc.oss.nimbus.domain.model.config.internal; @@ -12,34 +24,24 @@ import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.CollectionUtils; +import com.antheminc.oss.nimbus.domain.defn.Execution.Let; import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; - /** * @author Rakesh Patel + * @author Tony Lopez * */ -@AllArgsConstructor public class DefaultExecutionConfig implements ExecutionConfig { - @Getter @Setter - private Context context; private List execConfigs; - public DefaultExecutionConfig() { - this.context = new Context(); - this.execConfigs = new ArrayList<>(); - } - public void add(Annotation configAnnotation) { - execConfigs.add(configAnnotation); + getExecConfigs().add(configAnnotation); } public void addAll(Annotation[] configAnnotations) { - execConfigs.addAll(Arrays.asList(configAnnotations)); + getExecConfigs().addAll(Arrays.asList(configAnnotations)); } @Override @@ -55,7 +57,26 @@ public void sort() { Integer order1 = (Integer) AnnotationUtils.getAnnotationAttributes(o1).get("order"); Integer order2 = (Integer) AnnotationUtils.getAnnotationAttributes(o2).get("order"); - return order1.compareTo(order2); + int orderDifference = order1.compareTo(order2); + + // ensure Lets are evaluated first if order is the same + if (orderDifference == 0) { + if (o1 instanceof Let && !(o2 instanceof Let)) { + return -1; + } else if (o2 instanceof Let && !(o1 instanceof Let)) { + return 1; + } + return orderDifference; + } + + return orderDifference; }); } + + private List getExecConfigs() { + if (execConfigs == null) { + execConfigs = new ArrayList(); + } + return execConfigs; + } } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/state/extension/ConfigConditionalStateChangeHandler.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/state/extension/ConfigConditionalStateChangeHandler.java index 8f9e2168b..e78ab6d4b 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/state/extension/ConfigConditionalStateChangeHandler.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/state/extension/ConfigConditionalStateChangeHandler.java @@ -16,6 +16,8 @@ package com.antheminc.oss.nimbus.domain.model.state.extension; +import java.util.Arrays; + import org.apache.commons.lang3.ArrayUtils; import com.antheminc.oss.nimbus.InvalidConfigException; @@ -26,7 +28,6 @@ import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContextLoader; import com.antheminc.oss.nimbus.domain.defn.Execution.Config; import com.antheminc.oss.nimbus.domain.defn.extension.ConfigConditional; -import com.antheminc.oss.nimbus.domain.model.config.internal.DefaultExecutionConfig; import com.antheminc.oss.nimbus.domain.model.state.EntityState.Param; import com.antheminc.oss.nimbus.support.EnableLoggingInterceptor; @@ -79,10 +80,7 @@ protected void handleInternal(Param onChangeParam, ConfigConditional configur Command rootCmd = onChangeParam.getRootExecution().getRootCommand(); ExecutionContext eCtx = getContextLoader().load(rootCmd); - DefaultExecutionConfig executionConfig = new DefaultExecutionConfig(); - executionConfig.addAll(configs); - executionConfig.setContext(onChangeParam.getConfig().getExecutionConfig().getContext()); - getCommandGateway().executeConfig(eCtx, onChangeParam, executionConfig); + getCommandGateway().executeConfig(eCtx, onChangeParam, Arrays.asList(configs)); } // @Override diff --git a/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/SampleConfigPlaceholders.java b/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/SampleConfigVariables.java similarity index 78% rename from nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/SampleConfigPlaceholders.java rename to nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/SampleConfigVariables.java index 3f3f38cc6..3ee01befa 100644 --- a/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/SampleConfigPlaceholders.java +++ b/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/SampleConfigVariables.java @@ -15,11 +15,9 @@ */ package com.antheminc.oss.nimbus.test.scenarios.s0.view; -import java.util.List; - import com.antheminc.oss.nimbus.domain.defn.Execution.Config; -import com.antheminc.oss.nimbus.domain.defn.Execution.ConfigPlaceholder; import com.antheminc.oss.nimbus.domain.defn.Execution.DetourConfig; +import com.antheminc.oss.nimbus.domain.defn.Execution.Let; import com.antheminc.oss.nimbus.domain.defn.Model; import com.antheminc.oss.nimbus.domain.defn.extension.ConfigConditional; @@ -32,27 +30,27 @@ */ @Model @Getter @Setter -public class SampleConfigPlaceholders { +public class SampleConfigVariables { private String p0; - @ConfigPlaceholder(name = "foo", value = "/../p0") + @Let(name = "foo", value = "/../p0") @Config(url = "/_process?fn=_set&value=p1-did-it") private String p1; - @ConfigPlaceholder(name = "bar", value = "/../p0") - @ConfigPlaceholder(name = "baz", value = "p2-did-it") + @Let(name = "bar", value = "/../p0") + @Let(name = "baz", value = "p2-did-it") @Config(url = "/_process?fn=_set&value=") private String p2; @Config(url = "/_process?fn=_set&value=p3-did-it") private String p3; - @ConfigPlaceholder(name = "foo", value = "/../p0") + @Let(name = "foo", value = "/../p0") @DetourConfig(main = @Config(url = "break things"), onException = @Config(url = "/_process?fn=_set&value=p4-did-it")) private String p4; - @ConfigPlaceholder(name = "foo", value = "/../p0") + @Let(name = "foo", value = "/../p0") @ConfigConditional(when = "true", config = { @Config(url = "/_process?fn=_set&value=p5-did-it") }) @@ -62,7 +60,7 @@ public class SampleConfigPlaceholders { private String[] p7; - @ConfigPlaceholder(name = "foo", value = "/../p") + @Let(name = "foo", value = "/../p") @Config(url = "/../p7/_replace?rawPayload=[\"0\"]") @Config(url = "/_process?fn=_set&value=p8-did-it", col="") private String p8; diff --git a/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/VRSampleViewRootEntity.java b/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/VRSampleViewRootEntity.java index 4d2acfbfe..a26b55746 100644 --- a/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/VRSampleViewRootEntity.java +++ b/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/VRSampleViewRootEntity.java @@ -63,5 +63,5 @@ public class VRSampleViewRootEntity { private SampleParamStateHolders paramStateHolders; - private SampleConfigPlaceholders configPlaceholders; + private SampleConfigVariables configVariables; } diff --git a/nimbus-test/src/test/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/CommandPathVariableResolverTest.java b/nimbus-test/src/test/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/CommandPathVariableResolverTest.java index b9cd8d6ed..52caf9bd9 100644 --- a/nimbus-test/src/test/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/CommandPathVariableResolverTest.java +++ b/nimbus-test/src/test/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/CommandPathVariableResolverTest.java @@ -18,21 +18,16 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import java.lang.annotation.Annotation; - import org.junit.FixMethodOrder; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runners.MethodSorters; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mock.web.MockHttpServletRequest; import com.antheminc.oss.nimbus.domain.AbstractFrameworkIngerationPersistableTests; import com.antheminc.oss.nimbus.domain.cmd.Action; import com.antheminc.oss.nimbus.domain.cmd.exec.CommandExecution.MultiOutput; -import com.antheminc.oss.nimbus.domain.cmd.exec.CommandExecutorGateway; -import com.antheminc.oss.nimbus.domain.defn.Execution.ConfigPlaceholder; import com.antheminc.oss.nimbus.domain.model.state.EntityState.Param; import com.antheminc.oss.nimbus.support.Holder; import com.antheminc.oss.nimbus.test.domain.support.utils.ExtractResponseOutputUtils; @@ -43,10 +38,7 @@ * */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class CommandPathVariableResolverTest extends AbstractFrameworkIngerationPersistableTests { - - @Autowired - private CommandExecutorGateway commandExecutorGateway; +public class CommandPathVariableResolverTest extends AbstractFrameworkIngerationPersistableTests { @Rule public ExpectedException thrown = ExpectedException.none(); @@ -110,7 +102,7 @@ public void testConfigPlaceholderSingle() { Long refId = createOrGetDomainRoot_RefId(); MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) .addRefId(refId) - .addNested("/configPlaceholders/p1") + .addNested("/configVariables/p1") .addAction(Action._get) .getMock(); Holder resp = (Holder) controller.handleGet(req, null); @@ -125,7 +117,7 @@ public void testConfigPlaceholderMultiple() { Long refId = createOrGetDomainRoot_RefId(); MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) .addRefId(refId) - .addNested("/configPlaceholders/p2") + .addNested("/configVariables/p2") .addAction(Action._get) .getMock(); Holder resp = (Holder) controller.handleGet(req, null); @@ -142,7 +134,7 @@ public void testConfigPlaceholderUndefined() { Long refId = createOrGetDomainRoot_RefId(); MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) .addRefId(refId) - .addNested("/configPlaceholders/p3") + .addNested("/configVariables/p3") .addAction(Action._get) .getMock(); @@ -155,13 +147,13 @@ public void testConfigPlaceholderWithDetourConfigs() { Long refId = createOrGetDomainRoot_RefId(); MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) .addRefId(refId) - .addNested("/configPlaceholders/p4") + .addNested("/configVariables/p4") .addAction(Action._get) .getMock(); Holder resp = (Holder) controller.handleGet(req, null); // TODO Output not generated. Why? - Param p0 = resp.getState().getContext().getQuadModel().getView().findParamByPath("/configPlaceholders/p0"); + Param p0 = resp.getState().getContext().getQuadModel().getView().findParamByPath("/configVariables/p0"); assertEquals("p4-did-it", p0.getState()); } @@ -172,7 +164,7 @@ public void testConfigPlaceholderWithConfigConditional() { Long refId = createOrGetDomainRoot_RefId(); MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) .addRefId(refId) - .addNested("/configPlaceholders/p5") + .addNested("/configVariables/p5") .addAction(Action._get) .getMock(); @@ -191,13 +183,13 @@ public void testConfigPlaceholderWithCol() { Long refId = createOrGetDomainRoot_RefId(); MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) .addRefId(refId) - .addNested("/configPlaceholders/p8") + .addNested("/configVariables/p8") .addAction(Action._get) .getMock(); Holder resp = (Holder) controller.handleGet(req, null); // TODO Output not generated. Why? - Param p0 = resp.getState().getContext().getQuadModel().getView().findParamByPath("/configPlaceholders/p0"); + Param p0 = resp.getState().getContext().getQuadModel().getView().findParamByPath("/configVariables/p0"); assertEquals("p8-did-it", p0.getState()); } @@ -223,24 +215,4 @@ private Param excuteNewConfig() { Param p = ExtractResponseOutputUtils.extractOutput(resp); return p; } - - private static ConfigPlaceholder buildConfigPlaceholder(String name, String value) { - return new ConfigPlaceholder() { - - @Override - public Class annotationType() { - return ConfigPlaceholder.class; - } - - @Override - public String value() { - return value; - } - - @Override - public String name() { - return name; - } - }; - } } From fc1f3849f3f6fe2c7afeb2cf2a2b7d31adf929d5 Mon Sep 17 00:00:00 2001 From: Tony Lopez Date: Tue, 26 Mar 2019 15:41:09 -0500 Subject: [PATCH 3/3] let variables --- .../config/DefaultCoreBuilderConfig.java | 6 +- .../config/DefaultProcessConfig.java | 7 +- .../domain/cmd/exec/ExecutionContext.java | 32 ++-- .../DefaultCommandExecutorGateway.java | 10 +- .../ConfigVariableFunctionHandler.java | 58 +++---- .../oss/nimbus/domain/defn/Execution.java | 6 - ...riableValidator.java => LetValidator.java} | 4 +- .../internal/ExecutionConfigFactory.java | 6 +- ...r.java => LetExecutionConfigProvider.java} | 22 ++- .../s0/view/SampleConfigVariables.java | 4 + .../DefaultConfigVariableResolverTest.java | 144 ++++++++++++++++++ .../CommandPathVariableResolverTest.java | 105 ------------- 12 files changed, 235 insertions(+), 169 deletions(-) rename nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/{ConfigVariableValidator.java => LetValidator.java} (95%) rename nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/{ConfigVariableExecutionConfigProvider.java => LetExecutionConfigProvider.java} (64%) create mode 100644 nimbus-test/src/test/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigVariableResolverTest.java diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreBuilderConfig.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreBuilderConfig.java index 6fbffe0a6..8babec1ec 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreBuilderConfig.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultCoreBuilderConfig.java @@ -45,7 +45,7 @@ import com.antheminc.oss.nimbus.domain.config.builder.attributes.DefaultAnnotationAttributeHandler; import com.antheminc.oss.nimbus.domain.model.config.EntityConfig.Scope; import com.antheminc.oss.nimbus.domain.model.config.builder.EntityConfigBuilder; -import com.antheminc.oss.nimbus.domain.model.config.builder.internal.ConfigVariableExecutionConfigProvider; +import com.antheminc.oss.nimbus.domain.model.config.builder.internal.LetExecutionConfigProvider; import com.antheminc.oss.nimbus.domain.model.config.builder.internal.DefaultEntityConfigBuilder; import com.antheminc.oss.nimbus.domain.model.config.builder.internal.DefaultExecutionConfigProvider; import com.antheminc.oss.nimbus.domain.model.config.builder.internal.DetourExecutionConfigProvider; @@ -147,8 +147,8 @@ public DetourExecutionConfigProvider detourExecutionConfigProvider(BeanResolverS } @Bean - public ConfigVariableExecutionConfigProvider configVariableExecutionConfigProvider() { - return new ConfigVariableExecutionConfigProvider(); + public LetExecutionConfigProvider letExecutionConfigProvider() { + return new LetExecutionConfigProvider(); } @Bean(name="default.annotationConfigBuilder") diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultProcessConfig.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultProcessConfig.java index 8cd83dd05..0a8b5a2cd 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultProcessConfig.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultProcessConfig.java @@ -28,6 +28,7 @@ import com.antheminc.oss.nimbus.domain.bpm.activiti.ActivitiExpressionManager; import com.antheminc.oss.nimbus.domain.bpm.activiti.CommandExecutorTaskDelegate; import com.antheminc.oss.nimbus.domain.bpm.activiti.DefaultMongoProcessRepository; +import com.antheminc.oss.nimbus.domain.cmd.exec.CommandExecutorGateway; import com.antheminc.oss.nimbus.domain.cmd.exec.FunctionHandler; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.DefaultParamFunctionHandler; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.ReservedKeywordRegistry; @@ -141,8 +142,8 @@ public EvalFunctionHandler evalFunctionHandler(ExpressionManager expression return new EvalFunctionHandler(expressionManager); } - @Bean(name="default._process$execute?fn=_setConfigVariable") - public FunctionHandler configVariableFunctionHandler(ReservedKeywordRegistry reservedKeywordRegistry){ - return new ConfigVariableFunctionHandler<>(reservedKeywordRegistry); + @Bean(name="default._process$execute?fn=_setVariable") + public FunctionHandler configVariableFunctionHandler(ReservedKeywordRegistry reservedKeywordRegistry, SpelExpressionEvaluator expressionEvaluator, CommandExecutorGateway commandExecutorGateway) { + return new ConfigVariableFunctionHandler<>(reservedKeywordRegistry, expressionEvaluator, commandExecutorGateway); } } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ExecutionContext.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ExecutionContext.java index 2f2b2e90d..fbaefa724 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ExecutionContext.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ExecutionContext.java @@ -59,6 +59,27 @@ public ExecutionContext(CommandMessage commandMessage, QuadModel quadModel setQuadModel(quadModel); } + /** + *

Add a resolved variable value into the resolved variable cache. + * @param name the name to store the variable under + * @param value the value to store + * @return the previous value associated with {@code name}, or + * {@code null} if there was no mapping for {@code name}. + */ + public Object addVariable(String name, Object value) { + return this.resolvedVariableMap.put(name, value); + } + + /** + *

Add resolved variables into the resolved variable cache. + * @param resolvedVariableMap a map containing entries to add. + * @return the previous value associated with {@code name}, or + * {@code null} if there was no mapping for {@code name}. + */ + public void addVariables(Map resolvedVariableMap) { + this.resolvedVariableMap.putAll(resolvedVariableMap); + } + public String getId() { return getCommandMessage().getCommand().getRootDomainUri(); } @@ -119,15 +140,8 @@ public int hashCode() { return builder.hashCode(); } - /** - *

Set a resolve variable value into the resolved variable cache. - * @param name the name to store the variable under - * @param value the value to store - * @return the previous value associated with {@code name}, or - * {@code null} if there was no mapping for {@code name}. - */ - public Object setVariable(String name, Object value) { - return this.resolvedVariableMap.put(name, value); + public void reset() { + this.resolvedVariableMap.clear(); } @Override diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandExecutorGateway.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandExecutorGateway.java index 29393db02..cdea2d0ba 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandExecutorGateway.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/DefaultCommandExecutorGateway.java @@ -16,7 +16,6 @@ package com.antheminc.oss.nimbus.domain.cmd.exec.internal; import java.lang.annotation.Annotation; -import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -60,7 +59,6 @@ import com.antheminc.oss.nimbus.domain.config.builder.DomainConfigBuilder; import com.antheminc.oss.nimbus.domain.defn.Constants; import com.antheminc.oss.nimbus.domain.defn.Execution.Config; -import com.antheminc.oss.nimbus.domain.defn.Execution.Let; import com.antheminc.oss.nimbus.domain.defn.builder.internal.ExecutionConfigBuilder; import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig; import com.antheminc.oss.nimbus.domain.model.config.ModelConfig; @@ -269,12 +267,10 @@ public List executeConfig(ExecutionContext eCtx, Param cmdParam, // execute & add output to mOutput MultiOutput configOutput = executeConfig(eCtx.getCommandMessage().getCommand(), configCmdMsg); - if (Let.class.isAssignableFrom(ec.annotationType())) { - SimpleEntry entry = (SimpleEntry) configOutput.getSingleResult(); - eCtx.setVariable(entry.getKey(), entry.getValue()); - } else { - configExecOutputs.add(configOutput); + if (null != configOutput.getContext()) { + eCtx.addVariables(configOutput.getContext().getResolvedVariableMap()); } + configExecOutputs.add(configOutput); } } catch(Exception ex) { Config exceptionConfig = execConfigProvider.getException(ec, eCtx); diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/process/ConfigVariableFunctionHandler.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/process/ConfigVariableFunctionHandler.java index 80fb23968..e8ab03d86 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/process/ConfigVariableFunctionHandler.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/process/ConfigVariableFunctionHandler.java @@ -16,14 +16,17 @@ package com.antheminc.oss.nimbus.domain.cmd.exec.internal.process; import java.util.AbstractMap.SimpleEntry; +import java.util.stream.Stream; -import org.springframework.util.StringUtils; +import org.apache.commons.lang3.StringUtils; import com.antheminc.oss.nimbus.InvalidConfigException; +import com.antheminc.oss.nimbus.domain.cmd.exec.CommandExecutorGateway; import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContext; import com.antheminc.oss.nimbus.domain.cmd.exec.FunctionHandler; import com.antheminc.oss.nimbus.domain.cmd.exec.internal.ReservedKeywordRegistry; import com.antheminc.oss.nimbus.domain.model.state.EntityState.Param; +import com.antheminc.oss.nimbus.support.expr.SpelExpressionEvaluator; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -36,38 +39,43 @@ @Getter public class ConfigVariableFunctionHandler implements FunctionHandler { - public static final String ARG_CONFIG = "config"; public static final String ARG_NAME = "name"; public static final String ARG_SPEL = "spel"; public static final String ARG_VALUE = "value"; private final ReservedKeywordRegistry reservedKeywordRegistry; - + private final SpelExpressionEvaluator expressionEvaluator; + private final CommandExecutorGateway commandExecutorGateway; + @Override public Object execute(ExecutionContext eCtx, Param actionParameter) { validate(eCtx, actionParameter); String variableName = eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_NAME); Object variableValue = determineVariableValue(eCtx, actionParameter); + eCtx.addVariable(variableName, variableValue); return new SimpleEntry(variableName, variableValue); } - + private Object determineVariableValue(ExecutionContext eCtx, Param actionParameter) { - String instruction = eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_VALUE); - if (!StringUtils.isEmpty(instruction)) { - return instruction; - } - - instruction = eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_SPEL); - if (!StringUtils.isEmpty(instruction)) { - // TODO + String valueProviderString = getValue(eCtx); + if (!StringUtils.isEmpty(valueProviderString)) { + return valueProviderString; } - - instruction = eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_CONFIG); - if (!StringUtils.isEmpty(instruction)) { - // TODO + + valueProviderString = getSpel(eCtx); + if (!StringUtils.isEmpty(valueProviderString)) { + return getExpressionEvaluator().getValue(valueProviderString, actionParameter); } - - return null; + + throw new InvalidConfigException("Unable to determine variable value."); + } + + private String getSpel(ExecutionContext eCtx) { + return eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_SPEL); + } + + private String getValue(ExecutionContext eCtx) { + return eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_VALUE); } /** @@ -85,19 +93,17 @@ private void validate(ExecutionContext eCtx, Param actionParameter) { if (StringUtils.isEmpty(name)) { throw new InvalidConfigException("The argument \"" + ARG_NAME + "\" is required."); } - + if (getReservedKeywordRegistry().exists(name)) { throw new InvalidConfigException("The placeholder \"" + name + "\" is a reserved keyword. Replacing reserved placeholders is not allowed."); } - if (StringUtils.isEmpty(eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_VALUE))) { - if (StringUtils.isEmpty(eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_SPEL))) { - if (StringUtils.isEmpty(eCtx.getCommandMessage().getCommand().getFirstParameterValue(ARG_CONFIG))) { - throw new InvalidConfigException("At least one value is required for either: " + ARG_VALUE + ", " - + ARG_SPEL + ", or " + ARG_CONFIG + "."); - } - } + long numProvidedValues = Stream.of(getValue(eCtx), getSpel(eCtx)).filter(StringUtils::isNotEmpty) + .count(); + if (1 != numProvidedValues) { + throw new InvalidConfigException("One and only one value is required for either: " + ARG_VALUE + " or " + + ARG_SPEL + "."); } } } diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/Execution.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/Execution.java index 6f92703f0..4822767e6 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/Execution.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/Execution.java @@ -110,12 +110,6 @@ @Execution public @interface Let { - /** - *

A {@link Config} execution to resolve and use in place of - * {@link #name()} when an execution using this variable is resolved. - */ - Config[] config() default {}; - /** *

The name of the placeholder that should be used as an identifier * to resolve in a config url. diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/ConfigVariableValidator.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/LetValidator.java similarity index 95% rename from nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/ConfigVariableValidator.java rename to nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/LetValidator.java index d0fc3894a..074cd0616 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/ConfigVariableValidator.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/LetValidator.java @@ -22,13 +22,13 @@ import lombok.RequiredArgsConstructor; /** - *

Validator for {@link com.antheminc.oss.nimbus.domain.defn.Execution.ConfigPlaceholder} + *

Validator for {@link com.antheminc.oss.nimbus.domain.defn.Execution.Let} * * @author Tony Lopez * @since 1.3 */ @RequiredArgsConstructor -public class ConfigVariableValidator { +public class LetValidator { private final ReservedKeywordRegistry reservedKeywordRegistry; diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ExecutionConfigFactory.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ExecutionConfigFactory.java index bc3e1104f..6b300328e 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ExecutionConfigFactory.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ExecutionConfigFactory.java @@ -21,7 +21,7 @@ import com.antheminc.oss.nimbus.domain.defn.Execution.Config; import com.antheminc.oss.nimbus.domain.defn.Execution.DetourConfig; import com.antheminc.oss.nimbus.domain.defn.Execution.Let; -import com.antheminc.oss.nimbus.domain.defn.validaton.ConfigVariableValidator; +import com.antheminc.oss.nimbus.domain.defn.validaton.LetValidator; import com.antheminc.oss.nimbus.domain.model.config.ExecutionConfig; import com.antheminc.oss.nimbus.domain.model.config.internal.DefaultExecutionConfig; @@ -32,10 +32,10 @@ */ public class ExecutionConfigFactory { - private final ConfigVariableValidator configVariableValidator; + private final LetValidator configVariableValidator; public ExecutionConfigFactory(ReservedKeywordRegistry reservedKeywordRegistry) { - this.configVariableValidator = new ConfigVariableValidator(reservedKeywordRegistry); + this.configVariableValidator = new LetValidator(reservedKeywordRegistry); } public ExecutionConfig build(AnnotatedElement aElem) { diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ConfigVariableExecutionConfigProvider.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/LetExecutionConfigProvider.java similarity index 64% rename from nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ConfigVariableExecutionConfigProvider.java rename to nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/LetExecutionConfigProvider.java index 64eeb64bf..64b4a1dc7 100644 --- a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/ConfigVariableExecutionConfigProvider.java +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/LetExecutionConfigProvider.java @@ -15,9 +15,12 @@ */ package com.antheminc.oss.nimbus.domain.model.config.builder.internal; +import org.apache.commons.lang3.StringUtils; + import com.antheminc.oss.nimbus.domain.cmd.Action; import com.antheminc.oss.nimbus.domain.cmd.CommandMessage; import com.antheminc.oss.nimbus.domain.cmd.exec.ExecutionContext; +import com.antheminc.oss.nimbus.domain.defn.Constants; import com.antheminc.oss.nimbus.domain.defn.Execution.Config; import com.antheminc.oss.nimbus.domain.defn.Execution.Let; import com.antheminc.oss.nimbus.domain.defn.builder.internal.ExecutionConfigBuilder; @@ -27,15 +30,24 @@ * @author Tony Lopez * */ -public class ConfigVariableExecutionConfigProvider implements ExecutionConfigProvider { +public class LetExecutionConfigProvider implements ExecutionConfigProvider { @Override public Config getMain(Let configAnnotation, ExecutionContext eCtx) { final CommandMessage cmdMsg = eCtx.getCommandMessage(); - // TODO review and clean up. - final String valueString = "value=" + configAnnotation.value(); - String uri = "/p" + cmdMsg.getCommand().getAbsoluteDomainUri() + "/" + Action._process + "?fn=_setConfigVariable&name=" + configAnnotation.name() + "&" + valueString; - return ExecutionConfigBuilder.buildExecConfig(uri); + StringBuilder uri = new StringBuilder().append(Constants.SEPARATOR_URI_PLATFORM.code) + .append(cmdMsg.getCommand().getAbsoluteDomainUri()).append(Constants.SEPARATOR_URI.code) + .append(Action._process).append("?").append(Constants.KEY_FUNCTION.code).append("=_setVariable&name=") + .append(configAnnotation.name()); + + if (StringUtils.isNotEmpty(configAnnotation.value())) { + uri.append("&value=" + configAnnotation.value()); + } + if (StringUtils.isNotEmpty(configAnnotation.spel())) { + uri.append("&spel=" + configAnnotation.spel()); + } + + return ExecutionConfigBuilder.buildExecConfig(uri.toString()); } @Override diff --git a/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/SampleConfigVariables.java b/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/SampleConfigVariables.java index 3ee01befa..722eba8c9 100644 --- a/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/SampleConfigVariables.java +++ b/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/SampleConfigVariables.java @@ -64,4 +64,8 @@ public class SampleConfigVariables { @Config(url = "/../p7/_replace?rawPayload=[\"0\"]") @Config(url = "/_process?fn=_set&value=p8-did-it", col="") private String p8; + + @Let(name = "foo", spel = "state") + @Config(url = "/../p/_process?fn=_set&value=p9-did-it") + private String p9; } diff --git a/nimbus-test/src/test/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigVariableResolverTest.java b/nimbus-test/src/test/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigVariableResolverTest.java new file mode 100644 index 000000000..828844e5b --- /dev/null +++ b/nimbus-test/src/test/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigVariableResolverTest.java @@ -0,0 +1,144 @@ +/** + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.antheminc.oss.nimbus.domain.cmd.exec; + +import static org.junit.Assert.assertEquals; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.mock.web.MockHttpServletRequest; + +import com.antheminc.oss.nimbus.domain.AbstractFrameworkIngerationPersistableTests; +import com.antheminc.oss.nimbus.domain.cmd.Action; +import com.antheminc.oss.nimbus.domain.cmd.exec.CommandExecution.MultiOutput; +import com.antheminc.oss.nimbus.domain.model.state.EntityState.Param; +import com.antheminc.oss.nimbus.support.Holder; +import com.antheminc.oss.nimbus.test.domain.support.utils.MockHttpRequestBuilder; + +/** + * @author Tony Lopez + * + */ +public class DefaultConfigVariableResolverTest extends AbstractFrameworkIngerationPersistableTests { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @SuppressWarnings("unchecked") + @Test + public void testLetStaticReplacementSingle() { + Long refId = createOrGetDomainRoot_RefId(); + MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) + .addRefId(refId) + .addNested("/configVariables/p1") + .addAction(Action._get) + .getMock(); + Holder resp = (Holder) controller.handleGet(req, null); + Param p0 = (Param) resp.getState().findFirstParamOutputEndingWithPath("/p0").getValue(); + + assertEquals("p1-did-it", p0.getState()); + } + + @SuppressWarnings("unchecked") + @Test + public void testLetStaticReplacementMultiple() { + Long refId = createOrGetDomainRoot_RefId(); + MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) + .addRefId(refId) + .addNested("/configVariables/p2") + .addAction(Action._get) + .getMock(); + Holder resp = (Holder) controller.handleGet(req, null); + Param p0 = (Param) resp.getState().findFirstParamOutputEndingWithPath("/p0").getValue(); + + assertEquals("p2-did-it", p0.getState()); + } + + @Test + public void testLetStaticReplacementUndefined() { + thrown.expect(NumberFormatException.class); + thrown.expectMessage("For input string: \"1null\""); + + Long refId = createOrGetDomainRoot_RefId(); + MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) + .addRefId(refId) + .addNested("/configVariables/p3") + .addAction(Action._get) + .getMock(); + + controller.handleGet(req, null); + } + + @SuppressWarnings("unchecked") + @Test + public void testLetStaticReplacementWithDetourConfigs() { + Long refId = createOrGetDomainRoot_RefId(); + MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) + .addRefId(refId) + .addNested("/configVariables/p4") + .addAction(Action._get) + .getMock(); + + Holder resp = (Holder) controller.handleGet(req, null); + // TODO Output not generated. Why? + Param p0 = resp.getState().getContext().getQuadModel().getView().findParamByPath("/configVariables/p0"); + + assertEquals("p4-did-it", p0.getState()); + } + + @SuppressWarnings("unchecked") + @Test + public void testLetStaticReplacementWithCol() { + Long refId = createOrGetDomainRoot_RefId(); + MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) + .addRefId(refId) + .addNested("/configVariables/p8") + .addAction(Action._get) + .getMock(); + + Holder resp = (Holder) controller.handleGet(req, null); + // TODO Output not generated. Why? + Param p0 = resp.getState().getContext().getQuadModel().getView().findParamByPath("/configVariables/p0"); + + assertEquals("p8-did-it", p0.getState()); + } + + @SuppressWarnings("unchecked") + @Test + public void testLetSpelReplacement() { + Long refId = createOrGetDomainRoot_RefId(); + MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) + .addRefId(refId) + .addNested("/configVariables/p0") + .addAction(Action._get) + .getMock(); + + Holder resp = (Holder) controller.handleGet(req, null); + Param p0 = (Param) resp.getState().findFirstParamOutputEndingWithPath("/p0").getValue(); + + p0.findParamByPath("/../p9").setState("0"); + + MockHttpServletRequest req2 = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) + .addRefId(refId) + .addNested("/configVariables/p9") + .addAction(Action._get) + .getMock(); + controller.handleGet(req2, null); + + assertEquals("p9-did-it", p0.getState()); + } +} diff --git a/nimbus-test/src/test/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/CommandPathVariableResolverTest.java b/nimbus-test/src/test/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/CommandPathVariableResolverTest.java index 52caf9bd9..cebd34c38 100644 --- a/nimbus-test/src/test/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/CommandPathVariableResolverTest.java +++ b/nimbus-test/src/test/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/CommandPathVariableResolverTest.java @@ -19,17 +19,13 @@ import static org.junit.Assert.assertNotNull; import org.junit.FixMethodOrder; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.runners.MethodSorters; import org.springframework.mock.web.MockHttpServletRequest; import com.antheminc.oss.nimbus.domain.AbstractFrameworkIngerationPersistableTests; import com.antheminc.oss.nimbus.domain.cmd.Action; -import com.antheminc.oss.nimbus.domain.cmd.exec.CommandExecution.MultiOutput; import com.antheminc.oss.nimbus.domain.model.state.EntityState.Param; -import com.antheminc.oss.nimbus.support.Holder; import com.antheminc.oss.nimbus.test.domain.support.utils.ExtractResponseOutputUtils; import com.antheminc.oss.nimbus.test.domain.support.utils.MockHttpRequestBuilder; @@ -40,9 +36,6 @@ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class CommandPathVariableResolverTest extends AbstractFrameworkIngerationPersistableTests { - @Rule - public ExpectedException thrown = ExpectedException.none(); - @Test public void t0_nestedConfigPath() { Param p = excuteNewConfig(); @@ -96,104 +89,6 @@ public void t1_environmentProperty() { } - @SuppressWarnings("unchecked") - @Test - public void testConfigPlaceholderSingle() { - Long refId = createOrGetDomainRoot_RefId(); - MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) - .addRefId(refId) - .addNested("/configVariables/p1") - .addAction(Action._get) - .getMock(); - Holder resp = (Holder) controller.handleGet(req, null); - Param p0 = (Param) resp.getState().findFirstParamOutputEndingWithPath("/p0").getValue(); - - assertEquals("p1-did-it", p0.getState()); - } - - @SuppressWarnings("unchecked") - @Test - public void testConfigPlaceholderMultiple() { - Long refId = createOrGetDomainRoot_RefId(); - MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) - .addRefId(refId) - .addNested("/configVariables/p2") - .addAction(Action._get) - .getMock(); - Holder resp = (Holder) controller.handleGet(req, null); - Param p0 = (Param) resp.getState().findFirstParamOutputEndingWithPath("/p0").getValue(); - - assertEquals("p2-did-it", p0.getState()); - } - - @Test - public void testConfigPlaceholderUndefined() { - thrown.expect(NumberFormatException.class); - thrown.expectMessage("For input string: \"1null\""); - - Long refId = createOrGetDomainRoot_RefId(); - MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) - .addRefId(refId) - .addNested("/configVariables/p3") - .addAction(Action._get) - .getMock(); - - controller.handleGet(req, null); - } - - @SuppressWarnings("unchecked") - @Test - public void testConfigPlaceholderWithDetourConfigs() { - Long refId = createOrGetDomainRoot_RefId(); - MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) - .addRefId(refId) - .addNested("/configVariables/p4") - .addAction(Action._get) - .getMock(); - - Holder resp = (Holder) controller.handleGet(req, null); - // TODO Output not generated. Why? - Param p0 = resp.getState().getContext().getQuadModel().getView().findParamByPath("/configVariables/p0"); - - assertEquals("p4-did-it", p0.getState()); - } - - @SuppressWarnings("unchecked") - @Test - public void testConfigPlaceholderWithConfigConditional() { - Long refId = createOrGetDomainRoot_RefId(); - MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) - .addRefId(refId) - .addNested("/configVariables/p5") - .addAction(Action._get) - .getMock(); - - Holder resp = (Holder) controller.handleGet(req, null); - Param p5 = (Param) resp.getState().findFirstParamOutputEndingWithPath("/p5").getValue(); - - p5.setState("changeit"); - - Param p0 = p5.findParamByPath("/../p0"); - assertEquals("p5-did-it", p0.getState()); - } - - @SuppressWarnings("unchecked") - @Test - public void testConfigPlaceholderWithCol() { - Long refId = createOrGetDomainRoot_RefId(); - MockHttpServletRequest req = MockHttpRequestBuilder.withUri(VIEW_PARAM_ROOT) - .addRefId(refId) - .addNested("/configVariables/p8") - .addAction(Action._get) - .getMock(); - - Holder resp = (Holder) controller.handleGet(req, null); - // TODO Output not generated. Why? - Param p0 = resp.getState().getContext().getQuadModel().getView().findParamByPath("/configVariables/p0"); - - assertEquals("p8-did-it", p0.getState()); - } - private void execGet(Long refId, String nestedPath) { final MockHttpServletRequest req = MockHttpRequestBuilder.withUri(CORE_NESTED_CONFIG_ROOT) .addRefId(refId)