changeset 903:8b26804049d4

HttpMessageModificationFilter - $lookup operator
author Devel 2
date Wed, 18 Apr 2018 10:47:13 +0200
parents f8b01b4c713f
children ede69c8df2fa
files stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationFilter.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTransformer.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTransformerTest.java
diffstat 4 files changed, 131 insertions(+), 67 deletions(-) [+]
line wrap: on
line diff
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationFilter.java	Wed Apr 18 09:02:17 2018 +0200
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationFilter.java	Wed Apr 18 10:47:13 2018 +0200
@@ -11,6 +11,7 @@
 import com.passus.data.ByteString;
 import com.passus.filter.UnmutableValueExtractor;
 import com.passus.filter.ValueExtractor;
+import com.passus.lookup.filter.LookupValueExtractorDefinitionCreator;
 import com.passus.net.http.HttpMessageHelper;
 import com.passus.net.http.HttpParameters;
 import com.passus.net.http.HttpRequest;
@@ -20,17 +21,17 @@
 import com.passus.st.config.HeaderOperationNodeDefinition;
 import com.passus.st.plugin.PluginConstants;
 import com.passus.st.validation.HeaderNameValidator;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
 import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
 
 import static com.passus.config.schema.ConfigurationSchemaBuilder.*;
 
 /**
- *
  * @author Mirosław Hawrot
  */
 @NodeDefinitionCreate(HttpMessageModificationFilter.HttpMessageModificationFilterNodeDefCreator.class)
@@ -603,12 +604,15 @@
 
         @Override
         public NodeDefinition create() {
+            NodeDefinition lookupNodeDef = LookupValueExtractorDefinitionCreator.createNodeDef();
+
             NodeDefinition paramNodeDef = new HeaderOperationNodeDefinition(
                     mixedDef(
                             valueDef(),
                             listDef(valueDef()),
                             mapDef(
-                                    tupleDef("$expr", valueDef())
+                                    tupleDef("$expr", valueDef()).setRequired(false),
+                                    tupleDef("$lookup", lookupNodeDef).setRequired(false)
                             )
                     )
             ).addParam(tupleDef("*escape", valueDefBool()).setRequired(false));
@@ -616,7 +620,10 @@
             HeaderOperationNodeDefinition headerNodeDef = new HeaderOperationNodeDefinition(
                     mixedDef(
                             valueDef(),
-                            mapDef(tupleDef("$expr", valueDef()))
+                            mapDef(
+                                    tupleDef("$expr", valueDef()).setRequired(false),
+                                    tupleDef("$lookup", lookupNodeDef).setRequired(false)
+                            )
                     )
             ).addParam(tupleDef("*escape", valueDefBool()).setRequired(false));
 
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTransformer.java	Wed Apr 18 09:02:17 2018 +0200
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTransformer.java	Wed Apr 18 10:47:13 2018 +0200
@@ -1,37 +1,32 @@
 package com.passus.st.client.http.filter;
 
-import com.passus.config.CListNode;
-import com.passus.config.CMapNode;
-import com.passus.config.CNode;
-import com.passus.config.CTupleNode;
-import com.passus.config.CValueNode;
-import com.passus.config.ConfigurationContext;
-import com.passus.config.ConfigurationUtils;
-import com.passus.config.NodeType;
+import com.passus.config.*;
 import com.passus.config.schema.NodeTransformer;
 import com.passus.config.validation.Errors;
 import com.passus.filter.ValueExtractor;
 import com.passus.filter.config.ExpressionNodeTransformer;
+import com.passus.lookup.Lookup;
+import com.passus.lookup.filter.LookupValueExtractorTransformer;
 import com.passus.st.client.http.extractor.ContentExtractorUtils;
 import com.passus.st.client.http.extractor.ContentReplacer;
 import com.passus.st.client.http.filter.HttpMessageModificationFilter.*;
 import com.passus.st.filter.Transformers;
 import com.passus.st.utils.ConfigurationContextConsts;
 
-import static com.passus.st.validation.NodeValidationUtils.validateType;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
+import static com.passus.st.validation.NodeValidationUtils.validateType;
+
 /**
  * @author Mirosław Hawrot
  */
 public class HttpMessageModificationFilterTransformer implements NodeTransformer {
 
     private static Operation createNameOperation(CTupleNode tuple,
-            Errors errors, Class<? extends Operation> clazz) {
+                                                 Errors errors, Class<? extends Operation> clazz) {
         if (validateType(tuple.getNode(), NodeType.VALUE, errors)) {
             CValueNode valNode = (CValueNode) tuple.getNode();
             try {
@@ -46,9 +41,53 @@
         return null;
     }
 
+    private static ValueExtractor createValueExtractor(CNode node, Errors errors,
+                                                       ExpressionNodeTransformer expressionNodeTransformer,
+                                                       LookupValueExtractorTransformer lookupValueTransformer) {
+        NodeType type = node.getType();
+        ValueExtractor valueExtractor = null;
+        if (type == NodeType.VALUE) {
+            try {
+                valueExtractor = expressionNodeTransformer.transform(node);
+            } catch (Exception e) {
+                errors.reject("Invalid expression.", node);
+            }
+        } else if (type == NodeType.MAP) {
+            CMapNode mapNode = (CMapNode) node;
+            if (mapNode.size() != 1) {
+                errors.reject("Invalid node.", node);
+                return null;
+            }
+
+            CTupleNode tupleNode = mapNode.getFirstChild();
+            String opName = tupleNode.getName();
+            if (opName.equals("$expr")) {
+                try {
+                    valueExtractor = expressionNodeTransformer.transform(node);
+                } catch (Exception e) {
+                    errors.reject("Invalid expression.", node);
+                }
+            } else if (opName.equals("$lookup")) {
+                CNode valNode = tupleNode.getNode();
+                try {
+                    valueExtractor = (ValueExtractor) lookupValueTransformer.transform(valNode);
+                } catch(NodeConversionException e) {
+                    errors.reject("Invalid lookup definition. " + e.getMessage(), valNode);
+                } catch(Exception e) {
+                    errors.reject("Invalid lookup definition.", valNode);
+                }
+            }
+        } else {
+            errors.reject("Invalid node.", node);
+        }
+
+        return valueExtractor;
+    }
+
     private static AbstractNameValueOperation createNameValueOperation(CTupleNode nodeTuple,
-            Errors errors, Class<? extends AbstractNameValueOperation> clazz,
-            ExpressionNodeTransformer expressionNodeTransformer) {
+                                                                       Errors errors, Class<? extends AbstractNameValueOperation> clazz,
+                                                                       ExpressionNodeTransformer expressionNodeTransformer,
+                                                                       LookupValueExtractorTransformer lookupValueTransformer) {
 
         AbstractNameValueOperation op = null;
         if (validateType(nodeTuple.getNode(), NodeType.MAP, errors)) {
@@ -64,19 +103,12 @@
                         break;
                     default:
                         CNode valNode = tuple.getNode();
-                        NodeType type = valNode.getType();
-                        if (type == NodeType.VALUE || type == NodeType.MAP) {
-                            try {
-                                valueExtractor = expressionNodeTransformer.transform(valNode);
-                                field = name;
-                            } catch (Exception e) {
-                                errors.reject("Invalid expression.", valNode);
-                                return null;
-                            }
-                        } else {
-                            errors.reject("Invalid node.", valNode);
+                        valueExtractor = createValueExtractor(valNode, errors, expressionNodeTransformer, lookupValueTransformer);
+                        if (valueExtractor == null) {
                             return null;
                         }
+
+                        field = name;
                 }
 
                 if (errors.hasError()) {
@@ -100,8 +132,9 @@
     }
 
     private static AddParamOperation createAddParamOperation(CTupleNode nodeTuple,
-            Errors errors, Class<? extends AddParamOperation> clazz,
-            ExpressionNodeTransformer expressionNodeTransformer) {
+                                                             Errors errors, Class<? extends AddParamOperation> clazz,
+                                                             ExpressionNodeTransformer expressionNodeTransformer,
+                                                             LookupValueExtractorTransformer lookupValueTransformer) {
 
         AddParamOperation op = null;
         if (validateType(nodeTuple.getNode(), NodeType.MAP, errors)) {
@@ -118,18 +151,10 @@
                     default:
                         paramName = tuple.getName();
                         CNode valNode = tuple.getNode();
-                        NodeType type = valNode.getType();
-                        if (type == NodeType.VALUE || type == NodeType.MAP) {
-                            try {
-                                valueExtractor = expressionNodeTransformer.transform(valNode);
-                            } catch (Exception e) {
-                                errors.reject("Invalid expression.", valNode);
-                                return null;
-                            }
-                        } else {
-                            throw new RuntimeException("Value or Map node required.");
+                        valueExtractor = createValueExtractor(valNode, errors, expressionNodeTransformer, lookupValueTransformer);
+                        if (valueExtractor == null) {
+                            return null;
                         }
-                        break;
                 }
 
                 if (errors.hasError()) {
@@ -164,7 +189,9 @@
         }
 
         Map<String, ValueExtractor> vars = (Map<String, ValueExtractor>) context.get(ConfigurationContextConsts.APP_VARS);
-        final ExpressionNodeTransformer transformer = Transformers.expressionNodeTransformer(vars);
+        List<Lookup> lookups = context.get(ConfigurationContextConsts.LOOKUPS, Collections.EMPTY_LIST);
+        final ExpressionNodeTransformer expressionNodeTransformer = Transformers.expressionNodeTransformer(vars);
+        final LookupValueExtractorTransformer lookupValueTransformer = new LookupValueExtractorTransformer(lookups);
 
         for (CTupleNode tuple : tuples) {
             String opName = tuple.getName();
@@ -176,37 +203,37 @@
                         op = createNameOperation(tuple, errors, RemoveHeaderOperation.class);
                         break;
                     case "addheader":
-                        op = createNameValueOperation(tuple, errors, AddHeaderOperation.class, transformer);
+                        op = createNameValueOperation(tuple, errors, AddHeaderOperation.class, expressionNodeTransformer, lookupValueTransformer);
                         break;
                     case "setheader":
-                        op = createNameValueOperation(tuple, errors, SetHeaderOperation.class, transformer);
+                        op = createNameValueOperation(tuple, errors, SetHeaderOperation.class, expressionNodeTransformer, lookupValueTransformer);
                         break;
                     case "removecookie":
                         op = createNameOperation(tuple, errors, RemoveCookieOperation.class);
                         break;
                     case "addcookie":
-                        op = createNameValueOperation(tuple, errors, AddCookieOperation.class, transformer);
+                        op = createNameValueOperation(tuple, errors, AddCookieOperation.class, expressionNodeTransformer, lookupValueTransformer);
                         break;
                     case "setcookie":
-                        op = createNameValueOperation(tuple, errors, SetCookieOperation.class, transformer);
+                        op = createNameValueOperation(tuple, errors, SetCookieOperation.class, expressionNodeTransformer, lookupValueTransformer);
                         break;
                     case "removepostparam":
                         op = createNameOperation(tuple, errors, PostDataRemoveParamOperation.class);
                         break;
                     case "addpostparam":
-                        op = createAddParamOperation(tuple, errors, PostDataAddParamOperation.class, transformer);
+                        op = createAddParamOperation(tuple, errors, PostDataAddParamOperation.class, expressionNodeTransformer, lookupValueTransformer);
                         break;
                     case "setpostparam":
-                        op = createAddParamOperation(tuple, errors, PostDataSetParamOperation.class, transformer);
+                        op = createAddParamOperation(tuple, errors, PostDataSetParamOperation.class, expressionNodeTransformer, lookupValueTransformer);
                         break;
                     case "removequeryparam":
                         op = createNameOperation(tuple, errors, RemoveQueryParameterOperation.class);
                         break;
                     case "addqueryparam":
-                        op = createNameValueOperation(tuple, errors, AddQueryParameterOperation.class, transformer);
+                        op = createNameValueOperation(tuple, errors, AddQueryParameterOperation.class, expressionNodeTransformer, lookupValueTransformer);
                         break;
                     case "setqueryparam":
-                        op = createNameValueOperation(tuple, errors, SetQueryParameterOperation.class, transformer);
+                        op = createNameValueOperation(tuple, errors, SetQueryParameterOperation.class, expressionNodeTransformer, lookupValueTransformer);
                         break;
                     case "setcontent":
                         if (validateType(tuple.getNode(), NodeType.MAP, errors)) {
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTest.java	Wed Apr 18 09:02:17 2018 +0200
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTest.java	Wed Apr 18 10:47:13 2018 +0200
@@ -257,6 +257,7 @@
         Errors errors = new Errors();
         ConfigurationContextImpl emptyContext = new ConfigurationContextImpl();
         List<HttpFilter> filters = HttpFiltersConfigurator.getFilters(filterConfig, errors, emptyContext);
+        HttpFilterTestUtils.printErrors(errors);
 
         HttpMessageModificationFilter filter = (HttpMessageModificationFilter) filters.get(0);
         HttpFlowContext mockContext = HttpFilterTestUtils.createMockContext();
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTransformerTest.java	Wed Apr 18 09:02:17 2018 +0200
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTransformerTest.java	Wed Apr 18 10:47:13 2018 +0200
@@ -1,25 +1,29 @@
 package com.passus.st.client.http.filter;
 
+import com.passus.commons.utils.ReflectionUtils;
+import com.passus.config.*;
+import com.passus.config.validation.Errors;
+import com.passus.filter.UnmutableValueExtractor;
+import com.passus.filter.ValueExtractor;
+import com.passus.lookup.Lookup;
+import com.passus.lookup.LookupList;
+import com.passus.lookup.filter.LookupValueExtractor;
+import com.passus.st.client.http.filter.HttpMessageModificationFilter.*;
+import com.passus.st.utils.ConfigurationContextConsts;
+import org.testng.annotations.Test;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 import static com.passus.commons.collection.FluentBuilder.e;
 import static com.passus.commons.collection.FluentBuilder.map;
-import com.passus.commons.utils.ReflectionUtils;
-import com.passus.config.CMapNode;
-import com.passus.config.CTupleNode;
-import com.passus.config.CValueNode;
-import com.passus.config.ConfigurationContextImpl;
-import com.passus.config.YamlConfigurationReader;
-import com.passus.config.validation.Errors;
-import com.passus.filter.UnmutableValueExtractor;
-import com.passus.filter.ValueExtractor;
-import com.passus.st.client.http.filter.HttpMessageModificationFilter.*;
 import static com.passus.st.client.http.filter.HttpVarsFilterTest.expr;
 import static com.passus.st.client.http.filter.HttpVarsFilterTest.val;
-import com.passus.st.filter.HttpMessageWrapperDynamicExtractor;
-import com.passus.st.utils.ConfigurationContextConsts;
-import java.util.List;
-import java.util.Map;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 import static org.testng.AssertJUnit.*;
-import org.testng.annotations.Test;
 
 /**
  *
@@ -229,7 +233,6 @@
         ConfigurationContextImpl context = new ConfigurationContextImpl();
         context.add(ConfigurationContextConsts.APP_VARS, appVars);
         CValueNode value = transformer.transform(node.getNode(), errors, context);
-        HttpFilterTestUtils.printErrors(errors);
 
         List<Operation> operations = (List) value.getValue();
         assertEquals("req.url.path", getFieldName(operations.get(0)));
@@ -237,6 +240,32 @@
         assertEquals("value1", getExtractorValue(operations.get(2)));
     }
 
+    @Test
+    public void testTransform_lookups() throws Exception {
+        ConfigurationContextImpl context = new ConfigurationContextImpl();
+        Lookup lookup = mock(Lookup.class);
+        when(lookup.getName()).thenReturn("test");
+        when(lookup.lookup(any(Set.class), any(Lookup.KeyInfo.class))).thenReturn("testValue");
+
+        LookupList lookups  = new LookupList(lookup);
+        context.add(ConfigurationContextConsts.LOOKUPS, lookups);
+        String config = "operations:\n"
+                + "  setHeader:\n"
+                + "    H2: {$lookup: {name: 'test', keys: {col1: test}}}\n";
+        CTupleNode node = read(config);
+        Errors errors = new Errors();
+        CValueNode value = transformer.transform(node.getNode(), errors, context);
+
+        assertEquals(0, errors.getErrorCount());
+
+        List<Operation> operations = (List) value.getValue();
+        SetHeaderOperation operation = (SetHeaderOperation)operations.get(0);
+        assertTrue(operation.getValueExtractor() instanceof LookupValueExtractor);
+
+        LookupValueExtractor lookupValueExtractor = (LookupValueExtractor) operation.getValueExtractor();
+        assertSame(lookup, lookupValueExtractor.getLookup());
+    }
+
     private static boolean getReplacerEscape(Operation o) {
         return ReflectionUtils.getFieldByPath(o, "replacer.escape");
     }