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..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 @@ -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; @@ -44,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.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; @@ -125,8 +127,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 @@ -139,6 +146,11 @@ public DetourExecutionConfigProvider detourExecutionConfigProvider(BeanResolverS return new DetourExecutionConfigProvider(); } + @Bean + public LetExecutionConfigProvider letExecutionConfigProvider() { + return new LetExecutionConfigProvider(); + } + @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 e3c0ba9e2..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,6 +24,8 @@ 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.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.internal.DefaultActionExecutorConfig; @@ -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 ConfigVariableResolver configVariableResolver(ReservedKeywordRegistry reservedKeywordRegistry) { + return new DefaultConfigVariableResolver(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/app/extension/config/DefaultProcessConfig.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/app/extension/config/DefaultProcessConfig.java index 2b0dcb9e3..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,11 +28,14 @@ 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; 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 +142,8 @@ public EvalFunctionHandler evalFunctionHandler(ExpressionManager expression return new EvalFunctionHandler(expressionManager); } + @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/ConfigVariableResolver.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ConfigVariableResolver.java new file mode 100644 index 000000000..7586428ad --- /dev/null +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/ConfigVariableResolver.java @@ -0,0 +1,35 @@ +/** + * 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; + +/** + * @author Tony Lopez + * @since 1.3 + * + */ +public interface ConfigVariableResolver { + + /** + *

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 + * variables + * @param pathToResolve the path to resolve against + * @return the resolved path + */ + String resolve(ExecutionContext eCtx, String pathToResolve); + +} diff --git a/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigVariableResolver.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigVariableResolver.java new file mode 100644 index 000000000..8f487a7ef --- /dev/null +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/DefaultConfigVariableResolver.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 lombok.RequiredArgsConstructor; + +/** + * @author Tony Lopez + * @since 1.3 + * + */ +@RequiredArgsConstructor +public class DefaultConfigVariableResolver implements ConfigVariableResolver { + + private final ReservedKeywordRegistry reservedKeywordRegistry; + + @Override + 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(ParamPathExpressionParser.stripPrefixSuffix(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 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..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 @@ -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,26 +43,65 @@ 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); } + /** + *

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(); } + /** + *

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 +118,6 @@ public

P findStateByPath(String path) { return getRootModel().findStateByPath(path); } - @Override public boolean equals(Object other) { if(other==null) @@ -99,6 +140,10 @@ public int hashCode() { return builder.hashCode(); } + public void reset() { + this.resolvedVariableMap.clear(); + } + @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 d3182a5da..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 @@ -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. @@ -51,6 +51,7 @@ 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; @@ -94,6 +95,8 @@ public class DefaultCommandExecutorGateway extends BaseCommandExecutorStrategies private CommandPathVariableResolver pathVariableResolver; + private ConfigVariableResolver configVariableResolver; + 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.configVariableResolver = getBeanResolver().get(ConfigVariableResolver.class); } @@ -235,7 +239,6 @@ protected void validateCommand(CommandMessage cmdMsg) { cmdMsg.getCommand().validate(); } - @SuppressWarnings("unchecked") @Override public List executeConfig(ExecutionContext eCtx, Param cmdParam, List execConfigs) { final CommandMessage cmdMsg = eCtx.getCommandMessage(); @@ -244,7 +247,7 @@ public List executeConfig(ExecutionContext eCtx, Param cmdParam, final List configExecOutputs = new ArrayList<>(); 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) @@ -254,24 +257,29 @@ public List executeConfig(ExecutionContext eCtx, Param cmdParam, buildAndExecuteColExecConfig(eCtx, cmdParam, 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 = getConfigVariableResolver().resolve(eCtx, 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 MultiOutput configOutput = executeConfig(eCtx.getCommandMessage().getCommand(), configCmdMsg); + if (null != configOutput.getContext()) { + eCtx.addVariables(configOutput.getContext().getResolvedVariableMap()); + } 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); + executeConfig(eCtx, cmdParam, Arrays.asList(exceptionConfig)); } }); @@ -324,8 +332,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 = 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())); } } else if(p.getConfig().getType().isArray()) { @@ -336,11 +345,12 @@ 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 = 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())); } } - + 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 3fd04b3c8..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 @@ -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,137 @@ @Getter(value=AccessLevel.PROTECTED) public class DefaultCommandPathVariableResolver implements CommandPathVariableResolver { - protected final JustLogit logit = new JustLogit(DefaultCommandPathVariableResolver.class); + public class ElemIdResolver extends Resolver { + + public ElemIdResolver() { + super(Constants.MARKER_ELEM_ID.code); + } + + @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 PassthroughResolver extends Resolver { + + 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() { + 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() { + super(Constants.MARKER_REF_ID.code); + } + + @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() { + super(Constants.MARKER_SESSION_SELF.code); + } + + @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() { + 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); 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 +199,25 @@ 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()); + 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) { + this.reservedKeywordRegistry.register(resolver.getKeyword()); + this.resolvers.add(resolver); + } @Override public String resolve(Param param, String urlToResolve) { @@ -92,72 +232,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 +300,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/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..e8ab03d86 --- /dev/null +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/cmd/exec/internal/process/ConfigVariableFunctionHandler.java @@ -0,0 +1,109 @@ +/** + * 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 java.util.stream.Stream; + +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; + +/** + * @author Tony Lopez + * + */ +@RequiredArgsConstructor +@Getter +public class ConfigVariableFunctionHandler implements FunctionHandler { + + 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 valueProviderString = getValue(eCtx); + if (!StringUtils.isEmpty(valueProviderString)) { + return valueProviderString; + } + + valueProviderString = getSpel(eCtx); + if (!StringUtils.isEmpty(valueProviderString)) { + return getExpressionEvaluator().getValue(valueProviderString, actionParameter); + } + + 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); + } + + /** + *

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."); + } + + 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 56f6ca015..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 @@ -24,10 +24,11 @@ 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; +import com.antheminc.oss.nimbus.domain.defn.Executions.LetMany; /** * @author Soham Chakravarti @@ -92,6 +93,49 @@ String when() default TRUE; } + /** + *

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 + */ + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.FIELD }) + @Repeatable(LetMany.class) + @Execution + public @interface Let { + + /** + *

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

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 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 * @@ -107,5 +151,4 @@ 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/LetValidator.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/LetValidator.java new file mode 100644 index 000000000..074cd0616 --- /dev/null +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/defn/validaton/LetValidator.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.Let; + +import lombok.RequiredArgsConstructor; + +/** + *

Validator for {@link com.antheminc.oss.nimbus.domain.defn.Execution.Let} + * + * @author Tony Lopez + * @since 1.3 + */ +@RequiredArgsConstructor +public class LetValidator { + + private final ReservedKeywordRegistry reservedKeywordRegistry; + + public boolean supports(Class clazz) { + return Let.class.isAssignableFrom(clazz); + } + + public void validate(Object 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 4059a347b..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 @@ -13,5 +13,4 @@ public interface ExecutionConfig { public List get(); - } 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/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 97e11b28a..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 @@ -1,33 +1,67 @@ /** - * + * 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 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.DetourConfig; +import com.antheminc.oss.nimbus.domain.defn.Execution.Let; +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; /** * @author Rakesh Patel + * @author Tony Lopez * */ public class ExecutionConfigFactory { - + + private final LetValidator configVariableValidator; + + public ExecutionConfigFactory(ReservedKeywordRegistry reservedKeywordRegistry) { + this.configVariableValidator = new LetValidator(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); - - executionConfig.addAll(arr); - executionConfig.addAll(arr2); - + DefaultExecutionConfig executionConfig = new DefaultExecutionConfig(); + buildConfigs(executionConfig, aElem); + buildDetourConfigs(executionConfig, aElem); + buildConfigVariables(executionConfig, aElem); executionConfig.sort(); - - return executionConfig; - + 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/builder/internal/LetExecutionConfigProvider.java b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/LetExecutionConfigProvider.java new file mode 100644 index 000000000..64b4a1dc7 --- /dev/null +++ b/nimbus-core/src/main/java/com/antheminc/oss/nimbus/domain/model/config/builder/internal/LetExecutionConfigProvider.java @@ -0,0 +1,58 @@ +/** + * 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.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; +import com.antheminc.oss.nimbus.domain.model.config.builder.ExecutionConfigProvider; + +/** + * @author Tony Lopez + * + */ +public class LetExecutionConfigProvider implements ExecutionConfigProvider { + + @Override + public Config getMain(Let configAnnotation, ExecutionContext eCtx) { + final CommandMessage cmdMsg = eCtx.getCommandMessage(); + 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 + public Config getException(Let configAnnotation, ExecutionContext eCtx) { + return null; + } + +} 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..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,40 +24,59 @@ 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; /** * @author Rakesh Patel + * @author Tony Lopez * */ public class DefaultExecutionConfig implements ExecutionConfig { - + private List execConfigs; + public void add(Annotation configAnnotation) { + getExecConfigs().add(configAnnotation); + } + public void addAll(Annotation[] configAnnotations) { - if(execConfigs == null) { - execConfigs = new ArrayList(); - } - execConfigs.addAll(Arrays.asList(configAnnotations)); - - + getExecConfigs().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"); - 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; }); } - - @Override - public List get() { + + 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 c9326abd4..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 @@ -28,8 +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.state.ExecutionTxnContext; -import com.antheminc.oss.nimbus.domain.model.state.ParamEvent; import com.antheminc.oss.nimbus.domain.model.state.EntityState.Param; import com.antheminc.oss.nimbus.support.EnableLoggingInterceptor; 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/SampleConfigVariables.java b/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/SampleConfigVariables.java new file mode 100644 index 000000000..722eba8c9 --- /dev/null +++ b/nimbus-test/src/main/java/com/antheminc/oss/nimbus/test/scenarios/s0/view/SampleConfigVariables.java @@ -0,0 +1,71 @@ +/** + * 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 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.Model; +import com.antheminc.oss.nimbus.domain.defn.extension.ConfigConditional; + +import lombok.Getter; +import lombok.Setter; + +/** + * @author Tony Lopez + * + */ +@Model +@Getter @Setter +public class SampleConfigVariables { + + private String p0; + + @Let(name = "foo", value = "/../p0") + @Config(url = "/_process?fn=_set&value=p1-did-it") + private String p1; + + @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; + + @Let(name = "foo", value = "/../p0") + @DetourConfig(main = @Config(url = "break things"), onException = @Config(url = "/_process?fn=_set&value=p4-did-it")) + private String p4; + + @Let(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; + + @Let(name = "foo", value = "/../p") + @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/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..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 @@ -62,4 +62,6 @@ public class VRSampleViewRootEntity { private List attr_list_2_simple; private SampleParamStateHolders paramStateHolders; + + private SampleConfigVariables configVariables; } 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 09aadc357..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 @@ -21,8 +21,6 @@ import org.junit.FixMethodOrder; import org.junit.Test; 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; @@ -36,10 +34,7 @@ * */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class CommandPathVariableResolverTest extends AbstractFrameworkIngerationPersistableTests { - - @Autowired - private PropertyResolver pr; +public class CommandPathVariableResolverTest extends AbstractFrameworkIngerationPersistableTests { @Test public void t0_nestedConfigPath() { @@ -93,7 +88,7 @@ public void t1_environmentProperty() { } - + private void execGet(Long refId, String nestedPath) { final MockHttpServletRequest req = MockHttpRequestBuilder.withUri(CORE_NESTED_CONFIG_ROOT) .addRefId(refId)