changeset 464:fa4e311fdf65

HttpScopeModificationFilter in progress
author Devel 2
date Wed, 02 Aug 2017 14:51:49 +0200
parents a98b900ee048
children 5401dadf33bb
files stress-tester/src/main/java/com/passus/st/client/http/HttpScopes.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpFiltersUtils.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpScopeModificationFilter.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpScopeModificationFilterTransformer.java stress-tester/src/main/java/com/passus/st/config/HeaderOperationNodeDefinition.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpScopeModificationFilterTest.java
diffstat 6 files changed, 483 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/stress-tester/src/main/java/com/passus/st/client/http/HttpScopes.java	Wed Aug 02 14:50:37 2017 +0200
+++ b/stress-tester/src/main/java/com/passus/st/client/http/HttpScopes.java	Wed Aug 02 14:51:49 2017 +0200
@@ -75,6 +75,19 @@
         return false;
     }
 
+    public boolean removeSession(HttpMessage msg) {
+        String zone = (String) msg.getTag(TAG_ZONE, ZONE_DEFAULT);
+        Map<String, ParametersBag> zoneSessions = getZoneSessions(zone, false);
+        if (zoneSessions != null) {
+            String sessionId = (String) msg.getTag(TAG_SESSION_ID);
+            if (sessionId != null) {
+                return (zoneSessions.remove(sessionId) != null);
+            }
+        }
+
+        return false;
+    }
+
     public ParametersBag getSession(String sessionId) {
         return HttpScopes.this.getSession(sessionId, ZONE_DEFAULT);
     }
@@ -83,6 +96,10 @@
         return HttpScopes.this.getSession(sessionId, zone, true);
     }
 
+    public ParametersBag getSession(String sessionId, boolean autocreate) {
+        return HttpScopes.this.getSession(sessionId, ZONE_DEFAULT, autocreate);
+    }
+
     public ParametersBag getSession(String sessionId, String zone, boolean autocreate) {
         Map<String, ParametersBag> zoneSessions = getZoneSessions(zone, autocreate);
         if (zoneSessions == null) {
@@ -112,6 +129,15 @@
         return getSession(sessionId, zone, autocreate);
     }
 
+    public ParametersBag getSession(HttpMessage msg, String zone, boolean autocreate) {
+        String sessionId = (String) msg.getTag(TAG_SESSION_ID);
+        if (sessionId == null) {
+            return null;
+        }
+
+        return getSession(sessionId, zone, autocreate);
+    }
+
     public boolean removeConversation(HttpRequest req) {
         String zone = (String) req.getTag(TAG_ZONE, ZONE_DEFAULT);
         return removeConversation(req.getId(), zone);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpFiltersUtils.java	Wed Aug 02 14:51:49 2017 +0200
@@ -0,0 +1,30 @@
+package com.passus.st.client.http.filter;
+
+import com.passus.filter.UnmutableValueExtractor;
+import com.passus.filter.ValueExtractor;
+import com.passus.net.http.HttpRequest;
+import com.passus.net.http.HttpResponse;
+import com.passus.st.client.http.HttpFlowContext;
+
+/**
+ *
+ * @author Mirosław Hawrot
+ */
+public class HttpFiltersUtils {
+
+    private HttpFiltersUtils() {
+    }
+
+    public static Object extractValue(ValueExtractor extractor, HttpRequest req, HttpResponse resp, HttpFlowContext context) {
+        Object value;
+        if (extractor instanceof UnmutableValueExtractor) {
+            value = extractor.extract(null);
+        } else {
+            HttpMessageWrapper wrapper = new HttpMessageWrapper(req, resp, context);
+            value = extractor.extract(wrapper);
+        }
+
+        return value;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpScopeModificationFilter.java	Wed Aug 02 14:51:49 2017 +0200
@@ -0,0 +1,201 @@
+package com.passus.st.client.http.filter;
+
+import com.passus.commons.Assert;
+import com.passus.commons.annotations.Plugin;
+import com.passus.config.Configuration;
+import com.passus.config.annotations.NodeDefinitionCreate;
+import static com.passus.config.schema.ConfigurationSchemaBuilder.mapDef;
+import static com.passus.config.schema.ConfigurationSchemaBuilder.nullValueDef;
+import static com.passus.config.schema.ConfigurationSchemaBuilder.tupleDef;
+import static com.passus.config.schema.ConfigurationSchemaBuilder.valueDef;
+import static com.passus.config.schema.ConfigurationSchemaBuilder.valueDefBool;
+import com.passus.config.schema.KeyNameVaryListNodeDefinition;
+import com.passus.config.schema.NodeDefinition;
+import com.passus.config.schema.NodeDefinitionCreator;
+import com.passus.filter.ValueExtractor;
+import com.passus.net.http.HttpRequest;
+import com.passus.net.http.HttpResponse;
+import com.passus.st.ParametersBag;
+import com.passus.st.client.http.HttpFlowContext;
+import com.passus.st.config.HeaderOperationNodeDefinition;
+import com.passus.st.plugin.PluginConstants;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author Mirosław Hawrot
+ */
+@NodeDefinitionCreate(HttpScopeModificationFilter.HttpScopeModificationFilterNodeDefCreator.class)
+@Plugin(name = HttpScopeModificationFilter.TYPE, category = PluginConstants.CATEGORY_HTTP_FILTER)
+public class HttpScopeModificationFilter extends HttpFilter {
+
+    public static final String TYPE = "modifyScope";
+
+    public static abstract class Operation {
+
+        public abstract void process(HttpRequest req, HttpResponse resp, HttpFlowContext context);
+
+    }
+
+    public static class RemoveSessionOperation extends Operation {
+
+        @Override
+        public void process(HttpRequest req, HttpResponse resp, HttpFlowContext context) {
+            context.scopes().removeSession(req);
+        }
+
+    }
+
+    public static class RemoveSessionParamOperation extends Operation {
+
+        private final String paramName;
+
+        public RemoveSessionParamOperation(String paramName) {
+            Assert.notNull(paramName, "paramName");
+            this.paramName = paramName;
+        }
+
+        @Override
+        public void process(HttpRequest req, HttpResponse resp, HttpFlowContext context) {
+            ParametersBag params = context.scopes().getSession(req, false);
+            if (params != null) {
+                params.remove(paramName);
+            }
+        }
+
+    }
+
+    public static class SetSessionParamOperation extends Operation {
+
+        protected final boolean autocreate;
+
+        protected final String paramName;
+
+        protected final ValueExtractor valueExtractor;
+
+        protected final boolean checkValueExists;
+
+        public SetSessionParamOperation(String paramName, ValueExtractor valueExtractor) {
+            this(paramName, valueExtractor, true);
+        }
+
+        public SetSessionParamOperation(String paramName, ValueExtractor valueExtractor, boolean checkValueExists) {
+            this(paramName, valueExtractor, checkValueExists, true);
+        }
+
+        public SetSessionParamOperation(String paramName, ValueExtractor valueExtractor, boolean checkValueExists, boolean autocreate) {
+            Assert.notNull(paramName, "paramName");
+            Assert.notNull(valueExtractor, "valueExtractor");
+
+            this.paramName = paramName;
+            this.valueExtractor = valueExtractor;
+            this.checkValueExists = checkValueExists;
+            this.autocreate = autocreate;
+        }
+
+        @Override
+        public void process(HttpRequest req, HttpResponse resp, HttpFlowContext context) {
+            ParametersBag params = context.scopes().getSession(req, autocreate);
+            if (params != null) {
+                if (checkValueExists && params.contains(paramName)) {
+                    return;
+                }
+
+                Object value = HttpFiltersUtils.extractValue(valueExtractor, req, resp, context);
+                params.set(paramName, value);
+            }
+        }
+
+    }
+
+    private final List<Operation> operations = new ArrayList<>();
+
+    private HttpMessagePredicate predicate;
+
+    public HttpMessagePredicate getPredicate() {
+        return predicate;
+    }
+
+    public void setPredicate(HttpMessagePredicate predicate) {
+        this.predicate = predicate;
+    }
+
+    public void addOperation(Operation operation) {
+        Assert.notNull(operation, "operation");
+        operations.add(operation);
+    }
+
+    public boolean removeOperation(Operation operation) {
+        Assert.notNull(operation, "operation");
+        return operations.remove(operation);
+    }
+
+    public void clearOperations() {
+        operations.clear();
+    }
+
+    @Override
+    public void configure(Configuration config) {
+        List<Operation> ops = (List<Operation>) config.get("operations");
+        operations.clear();
+
+        if (ops != null) {
+            for (Operation op : ops) {
+                addOperation(op);
+            }
+        }
+
+        predicate = (HttpMessagePredicate) config.get("applyIf", null);
+    }
+
+    @Override
+    public int filterOutbound(HttpRequest req, HttpResponse resp, HttpFlowContext context) {
+        if (req != null && !operations.isEmpty()) {
+            boolean exec = true;
+            if (predicate != null) {
+                HttpMessageWrapper wrapper = new HttpMessageWrapper(req, resp, context);
+                exec = predicate.test(wrapper);
+            }
+
+            if (exec) {
+                for (Operation op : operations) {
+                    op.process(req, resp, context);
+                }
+            }
+        }
+
+        return DUNNO;
+    }
+
+    @Override
+    public HttpScopeModificationFilter instanceForWorker(int index) {
+        HttpScopeModificationFilter filter = new HttpScopeModificationFilter();
+        filter.operations.addAll(operations);
+        filter.predicate = predicate;
+        return filter;
+    }
+
+    public static class HttpScopeModificationFilterNodeDefCreator implements NodeDefinitionCreator {
+
+        @Override
+        public NodeDefinition create() {
+            HeaderOperationNodeDefinition headerNodeDef = new HeaderOperationNodeDefinition(
+                    valueDef()
+            );
+            headerNodeDef.addParam(tupleDef("@autocreate", valueDefBool()).setRequired(false));
+
+            KeyNameVaryListNodeDefinition operationsDef = new KeyNameVaryListNodeDefinition()
+                    .setNodeTransformer(new HttpScopeModificationFilterTransformer())
+                    .add("$removeSession", nullValueDef())
+                    .add("$removeSessionParam", valueDef())
+                    .add("$addSessionParam", headerNodeDef)
+                    .add("$setSessionParam", headerNodeDef);
+
+            return mapDef(
+                    tupleDef("operations", operationsDef),
+                    tupleDef("applyIf", new HttpFilterMessagePredicateNodeDefinition()).setRequired(false)
+            );
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpScopeModificationFilterTransformer.java	Wed Aug 02 14:51:49 2017 +0200
@@ -0,0 +1,87 @@
+package com.passus.st.client.http.filter;
+
+import com.passus.config.CMapNode;
+import com.passus.config.CNode;
+import com.passus.config.CTupleNode;
+import com.passus.config.CValueNode;
+import static com.passus.config.ConfigurationUtils.extractBoolean;
+import com.passus.config.NodeType;
+import com.passus.config.schema.NodeTransformer;
+import com.passus.config.validation.Errors;
+import com.passus.st.client.http.filter.HttpScopeModificationFilter.Operation;
+import com.passus.st.client.http.filter.HttpScopeModificationFilter.RemoveSessionOperation;
+import com.passus.st.client.http.filter.HttpScopeModificationFilter.SetSessionParamOperation;
+import static com.passus.st.validation.NodeValidationUtils.validateType;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ *
+ * @author Mirosław Hawrot
+ */
+public class HttpScopeModificationFilterTransformer implements NodeTransformer {
+
+    private SetSessionParamOperation createSetSessionParamOperation(CTupleNode tuple, Errors errors) {
+        if (validateType(tuple.getNode(), NodeType.MAP, errors)) {
+            CMapNode mapNode = (CMapNode) tuple.getNode();
+            
+            boolean autocreate = false;
+            for(CTupleNode mapTupleNode: mapNode.getChildren()) {
+                switch(mapTupleNode.getName()) {
+                    case "@autocreate":
+                        autocreate = extractBoolean(mapTupleNode, errors);
+                        break;
+                    default:
+                        
+                } 
+            }
+        }
+        
+        return null;
+    }
+    
+    @Override
+    public Object transform(CNode node, Errors errors) {
+        CMapNode mapNode = (CMapNode) node;
+
+        List<CTupleNode> tuples = mapNode.getChildren();
+        List<Operation> operations;
+        if (tuples.isEmpty()) {
+            operations = Collections.EMPTY_LIST;
+        } else {
+            operations = new ArrayList<>();
+        }
+
+        for (CTupleNode tuple : tuples) {
+            String opName = tuple.getName();
+            try {
+                errors.pushNestedPath(opName);
+                Operation op = null;
+                switch (opName.toLowerCase()) {
+                    case "$removesession":
+                        op = new RemoveSessionOperation();
+                        break;
+                    case "$removesessionparam":
+                        break;
+                    case "$addsessionparam":
+                        break;
+                    case "$setsessionparam":
+                        break;
+                    default:
+                        throw new IllegalStateException("Not supported operation '" + opName + "'.");
+                }
+            } finally {
+                errors.popNestedPath();
+            }
+        }
+
+        return new CValueNode(operations);
+    }
+
+    @Override
+    public Object reverseTransform(CNode node, Errors errors) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+}
--- a/stress-tester/src/main/java/com/passus/st/config/HeaderOperationNodeDefinition.java	Wed Aug 02 14:50:37 2017 +0200
+++ b/stress-tester/src/main/java/com/passus/st/config/HeaderOperationNodeDefinition.java	Wed Aug 02 14:51:49 2017 +0200
@@ -65,7 +65,7 @@
         addParam(tupleDef);
         return this;
     }
-
+    
     public TupleNodeDefinition findParamTupleDef(String name) {
         if (paramsTuplesDef == null) {
             return null;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpScopeModificationFilterTest.java	Wed Aug 02 14:51:49 2017 +0200
@@ -0,0 +1,138 @@
+package com.passus.st.client.http.filter;
+
+import com.passus.config.validation.Errors;
+import com.passus.filter.UnmutableValueExtractor;
+import com.passus.net.http.HttpRequest;
+import com.passus.net.http.HttpRequestBuilder;
+import com.passus.st.AppUtils;
+import com.passus.st.client.http.HttpConsts;
+import com.passus.st.client.http.HttpFlowContext;
+import com.passus.st.client.http.HttpScopes;
+import static com.passus.st.client.http.filter.HttpScopeModificationFilter.*;
+import java.util.List;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.testng.AssertJUnit.*;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Mirosław Hawrot
+ */
+public class HttpScopeModificationFilterTest {
+
+    private final String sessionId = "sessionId";
+
+    @BeforeClass
+    public static void beforeClass() {
+        AppUtils.registerAll();
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        AppUtils.unregisterAll();
+    }
+
+    private HttpRequest createRequest() {
+        return HttpRequestBuilder.get("http://test.com/path")
+                .tag(HttpConsts.TAG_SESSION_ID, sessionId)
+                .build();
+    }
+
+    private HttpFlowContext createMockContext() {
+        HttpFlowContext mockContext = mock(HttpFlowContext.class);
+        final HttpScopes scopes = new HttpScopes();
+        when(mockContext.scopes()).thenReturn(scopes);
+        return mockContext;
+    }
+
+    @Test
+    public void testRemoveSessionOperation() {
+        HttpScopeModificationFilter filter = new HttpScopeModificationFilter();
+        filter.addOperation(new RemoveSessionOperation());
+
+        HttpFlowContext mockContext = createMockContext();
+        HttpScopes scopes = mockContext.scopes();
+        scopes.createSession(sessionId);
+        assertTrue(scopes.getSession(sessionId) != null);
+
+        HttpRequest req = createRequest();
+        filter.filterOutbound(req, null, mockContext);
+        assertTrue(scopes.getSession(sessionId, false) == null);
+    }
+
+    @Test
+    public void testRemoveSessionParamOperation() {
+        HttpScopeModificationFilter filter = new HttpScopeModificationFilter();
+        filter.addOperation(new RemoveSessionParamOperation("paramName"));
+
+        HttpFlowContext mockContext = createMockContext();
+        HttpScopes scopes = mockContext.scopes();
+        scopes.createSession(sessionId).set("paramName", "paramValue");
+        assertTrue(scopes.getSession(sessionId).contains("paramName"));
+
+        HttpRequest req = createRequest();
+        filter.filterOutbound(req, null, mockContext);
+        assertFalse(scopes.getSession(sessionId, false).contains("paramName"));
+    }
+
+    @Test
+    public void testSetSessionParamOperation_CheckValueExists_Autocreate() {
+        HttpScopeModificationFilter filter = new HttpScopeModificationFilter();
+        filter.addOperation(new SetSessionParamOperation("paramName", new UnmutableValueExtractor("paramValue"), true));
+
+        HttpFlowContext mockContext = createMockContext();
+        HttpScopes scopes = mockContext.scopes();
+
+        HttpRequest req = createRequest();
+        filter.filterOutbound(req, null, mockContext);
+        assertEquals("paramValue", scopes.getSession(sessionId, false).get("paramName"));
+
+        filter.clearOperations();
+        filter.addOperation(new SetSessionParamOperation("paramName", new UnmutableValueExtractor("newParamValue"), true));
+        filter.filterOutbound(req, null, mockContext);
+        assertEquals("paramValue", scopes.getSession(sessionId, false).get("paramName"));
+    }
+
+    @Test
+    public void testSetSessionParamOperation_NotCheckValueExists_Autocreate() {
+        HttpScopeModificationFilter filter = new HttpScopeModificationFilter();
+        filter.addOperation(new SetSessionParamOperation("paramName", new UnmutableValueExtractor("paramValue"), false));
+
+        HttpFlowContext mockContext = createMockContext();
+        HttpScopes scopes = mockContext.scopes();
+
+        HttpRequest req = createRequest();
+        filter.filterOutbound(req, null, mockContext);
+        assertEquals("paramValue", scopes.getSession(sessionId, false).get("paramName"));
+
+        filter.clearOperations();
+        filter.addOperation(new SetSessionParamOperation("paramName", new UnmutableValueExtractor("newParamValue"), false));
+        filter.filterOutbound(req, null, mockContext);
+        assertEquals("newParamValue", scopes.getSession(sessionId, false).get("paramName"));
+    }
+
+    @Test
+    public void testConfiguration() throws Exception {
+        String filterConfig = "filters:\n"
+                + "    - type: modifyScope\n"
+                + "      operations:\n"
+                + "        $removeSession: ~\n"
+                + "        $removeSessionParam: ParaName\n"
+                + "        $addSessionParam: \n"
+                + "           NewParam: NewParamValue\n"
+                + "        $setSessionParam: \n"
+                + "           \"@autocreate\": true\n"
+                + "           NewParam: NewParamValue \n";
+
+        Errors errors = new Errors();
+        List<HttpFilter> filters = HttpFiltersConfigurator.getFilters(filterConfig, errors);
+
+        errors.getAllErrors().forEach(System.out::println);
+        assertEquals(0, errors.getErrorCount());
+        assertEquals(1, filters.size());
+        assertTrue(filters.get(0) instanceof HttpScopeModificationFilter);
+    }
+}