Skip to content

Commit

Permalink
fix(groovy): Set the attribute based context as a context decorator.
Browse files Browse the repository at this point in the history
Some unit tests have been added.

Closes gravitee-io/issues#2455
  • Loading branch information
brasseld committed Sep 5, 2019
1 parent cc378de commit 7741b92
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 12 deletions.
3 changes: 2 additions & 1 deletion pom.xml
Expand Up @@ -38,7 +38,7 @@
<gravitee-policy-api.version>1.5.0</gravitee-policy-api.version>
<gravitee-common.version>1.15.0</gravitee-common.version>

<groovy-all.version>2.4.7</groovy-all.version>
<groovy-all.version>2.5.7</groovy-all.version>
<maven-assembly-plugin.version>2.5.5</maven-assembly-plugin.version>
</properties>

Expand Down Expand Up @@ -69,6 +69,7 @@
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>${groovy-all.version}</version>
<type>pom</type>
</dependency>

<dependency>
Expand Down
Expand Up @@ -15,20 +15,22 @@
*/
package io.gravitee.policy.groovy.utils;

import io.gravitee.el.TemplateEngine;
import io.gravitee.gateway.api.ExecutionContext;
import io.gravitee.gateway.api.Request;
import io.gravitee.gateway.api.Response;

import java.util.HashMap;
import java.util.Enumeration;
import java.util.Map;

/**
* @author David BRASSELY (david.brassely at graviteesource.com)
* @author GraviteeSource Team
*/
public class AttributesBasedExecutionContext {
public class AttributesBasedExecutionContext implements ExecutionContext {

private final static String CONTEXT_DICTIONARIES_VARIABLE = "dictionaries";
private final ExecutionContext context;
private final Map<String, Object> attributes = new AttributeMap();

public AttributesBasedExecutionContext(final ExecutionContext context) {
this.context = context;
Expand All @@ -39,18 +41,48 @@ public Map<String, Map<String, String>> getDictionaries() {
this.context.getTemplateEngine().getTemplateContext().lookupVariable(CONTEXT_DICTIONARIES_VARIABLE);
}

public Object getAttributes() {
return attributes;
@Override
public Request request() {
return context.request();
}

private class AttributeMap extends HashMap<String, Object> {
@Override
public Response response() {
return context.response();
}

@Override
public <T> T getComponent(Class<T> aClass) {
return context.getComponent(aClass);
}

AttributeMap() { }
@Override
public void setAttribute(String s, Object o) {
context.setAttribute(s, o);
}

@Override
public void removeAttribute(String s) {
context.removeAttribute(s);
}

@Override
public Object get(Object key) {
return context.getAttribute((String) key);
}
@Override
public Object getAttribute(String s) {
return context.getAttribute(s);
}

@Override
public Enumeration<String> getAttributeNames() {
return context.getAttributeNames();
}

@Override
public Map<String, Object> getAttributes() {
return context.getAttributes();
}

@Override
public TemplateEngine getTemplateEngine() {
return context.getTemplateEngine();
}
}
154 changes: 154 additions & 0 deletions src/test/java/io/gravitee/policy/groovy/GroovyPolicyTest.java
@@ -0,0 +1,154 @@
/**
* Copyright (C) 2015 The Gravitee team (http://gravitee.io)
*
* 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 io.gravitee.policy.groovy;

import io.gravitee.common.http.HttpHeaders;
import io.gravitee.common.http.HttpStatusCode;
import io.gravitee.gateway.api.ExecutionContext;
import io.gravitee.gateway.api.Request;
import io.gravitee.gateway.api.Response;
import io.gravitee.policy.api.PolicyChain;
import io.gravitee.policy.api.PolicyResult;
import io.gravitee.policy.groovy.configuration.GroovyPolicyConfiguration;
import io.gravitee.reporter.api.http.Metrics;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.io.*;
import java.nio.charset.Charset;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

/**
* @author David BRASSELY (david.brassely at graviteesource.com)
* @author GraviteeSource Team
*/
public class GroovyPolicyTest {

@Mock
private ExecutionContext executionContext;

@Mock
private Request request;

@Mock
private Response response;

@Mock
private PolicyChain policyChain;

@Mock
private GroovyPolicyConfiguration configuration;

@Before
public void init() {
MockitoAnnotations.initMocks(this);
when(request.metrics()).thenReturn(Metrics.on(System.currentTimeMillis()).build());
}

@Test
public void shouldFail_invalidScript() throws Exception {
when(configuration.getOnRequestScript()).thenReturn(loadResource("invalid_script.groovy"));
new GroovyPolicy(configuration).onRequest(request, response, executionContext, policyChain);

verify(policyChain, times(1)).failWith(any(PolicyResult.class));
}

/**
* Doc example: https://docs.gravitee.io/apim_policies_groovy.html#description
*/
@Test
public void shouldModifyResponseHeaders() throws Exception {
HttpHeaders headers = spy(new HttpHeaders());
when(response.headers()).thenReturn(headers);
when(configuration.getOnRequestScript()).thenReturn(loadResource("modify_response_headers.groovy"));
new GroovyPolicy(configuration).onRequest(request, response, executionContext, policyChain);

verify(headers, times(1)).remove(eq("X-Powered-By"));
verify(headers, times(1)).set(eq("X-Gravitee-Gateway-Version"), eq("0.14.0"));
verify(policyChain, times(1)).doNext(request, response);
}

/**
* Issue: https://github.com/gravitee-io/issues/issues/2455
*/
@Test
public void shouldSetContextAttribute() throws Exception {
when(configuration.getOnRequestScript()).thenReturn(loadResource("set_context_attribute.groovy"));
new GroovyPolicy(configuration).onRequest(request, response, executionContext, policyChain);

verify(executionContext, times(1)).setAttribute(eq("anyKey"), eq(0));
verify(policyChain, times(1)).doNext(request, response);
}

/**
* Doc example: https://docs.gravitee.io/apim_policies_groovy.html#onrequest_onresponse
* First run does not break the request.
*/
@Test
public void shouldNotBreakRequest() throws Exception {
HttpHeaders headers = spy(new HttpHeaders());
when(request.headers()).thenReturn(headers);

when(configuration.getOnRequestScript()).thenReturn(loadResource("break_request.groovy"));

new GroovyPolicy(configuration).onRequest(request, response, executionContext, policyChain);
verify(headers, times(1)).set(eq("X-Groovy-Policy"), eq("ok"));
verify(policyChain, times(1)).doNext(request, response);
}

/**
* Doc example: https://docs.gravitee.io/apim_policies_groovy.html#onrequest_onresponse
* Second run must break because of HTTP headers
*/
@Test
public void shouldBreakRequest() throws Exception {
HttpHeaders headers = spy(new HttpHeaders());
when(request.headers()).thenReturn(headers);

when(configuration.getOnRequestScript()).thenReturn(loadResource("break_request.groovy"));

headers.set("X-Gravitee-Break", "value");
new GroovyPolicy(configuration).onRequest(request, response, executionContext, policyChain);

verify(policyChain, times(1)).failWith(argThat(
result ->
result.statusCode() == HttpStatusCode.INTERNAL_SERVER_ERROR_500 &&
result.message().equals("Stop request processing due to X-Gravitee-Break header")));
}

private String loadResource(String resource) throws IOException {
InputStream stream = GroovyPolicy.class.getResourceAsStream(resource);
return readInputStreamToString(stream, Charset.defaultCharset());
}

private String readInputStreamToString(InputStream stream, Charset defaultCharset) throws IOException {
StringBuilder builder = new StringBuilder();
try (Reader reader = new BufferedReader(new InputStreamReader(stream, defaultCharset))) {
int c = 0;
while ((c = reader.read()) != -1) {
builder.append((char) c);
}
}

return builder.toString();
}


}
@@ -0,0 +1,9 @@
import io.gravitee.policy.groovy.PolicyResult.State

if (request.headers.containsKey('X-Gravitee-Break')) {
result.state = State.FAILURE
result.code = 500
result.error = 'Stop request processing due to X-Gravitee-Break header'
} else {
request.headers.'X-Groovy-Policy' = 'ok'
}
@@ -0,0 +1 @@
this is an error script just for testing purpose
@@ -0,0 +1,2 @@
response.headers.remove 'X-Powered-By'
response.headers.'X-Gravitee-Gateway-Version' = '0.14.0'
@@ -0,0 +1 @@
context.setAttribute('anyKey',0)

0 comments on commit 7741b92

Please sign in to comment.