Skip to content

Commit a318a11

Browse files
committed
More tests and bug fixes
More unit tests for #15 and added a unit test to verify #16. Updated readme to include servlet filter section
1 parent ee67106 commit a318a11

File tree

13 files changed

+145
-35
lines changed

13 files changed

+145
-35
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,15 @@ public String echoServletHeaders(@Context HttpServletRequest context) {
138138
return "servlet";
139139
}
140140
```
141+
142+
## Servlet Filters
143+
You can register [`Filter`](https://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html) implementations by implementing a `StartupsHandler` as defined in the `AwsLambdaServletContainerHandler` class. The `onStartup` methods receives a reference to the current `ServletContext`.
144+
145+
```java
146+
handler.setStartupHandler(c -> {
147+
FilterRegistration.Dynamic registration = c.addFilter("CustomHeaderFilter", CustomHeaderFilter.class);
148+
// update the registration to map to a path
149+
registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
150+
// servlet name mappings are disabled and will throw an exception
151+
});
152+
```

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsLambdaServletContainerHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ protected AwsLambdaServletContainerHandler(RequestReader<RequestType, ContainerR
6767
* Sets the ServletContext in the handler and initialized a new <code>FilterChainManager</code>
6868
* @param context An initialized ServletContext
6969
*/
70-
protected void setServletContext(ServletContext context) {
70+
protected void setServletContext(final ServletContext context) {
7171
servletContext = context;
7272
// We assume custom implementations of the RequestWriter for HttpServletRequest will reuse
7373
// the existing AwsServletContext object since it has no dependencies other than the Lambda context
@@ -114,7 +114,7 @@ public ServletContext getServletContext() {
114114
* </pre>
115115
* @param h A lambda expression that implements the <code>StartupHandler</code> functional interface
116116
*/
117-
public void setStartupHandler(StartupHandler h) {
117+
public void setStartupHandler(final StartupHandler h) {
118118
startupHandler = h;
119119
}
120120

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/FilterChainHolder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
9797
if (filters == null || filters.size() == 0 || currentFilter > filters.size() - 1) {
9898
return;
9999
}
100-
// TODO: We do check for async filters here
100+
// TODO: We do not check for async filters here
101+
101102
FilterHolder holder = filters.get(currentFilter);
102103

103104
if (!holder.isFilterInitialized()) {

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/FilterChainManager.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ protected FilterChainManager(ServletContextType context) {
7676
* @param request The incoming servlet request
7777
* @return A <code>FilterChainHolder</code> object that can be used to apply the filters to the request
7878
*/
79-
public FilterChainHolder getFilterChain(HttpServletRequest request) {
79+
public FilterChainHolder getFilterChain(final HttpServletRequest request) {
8080
String targetPath = request.getServletPath();
8181
DispatcherType type = request.getDispatcherType();
8282

@@ -130,7 +130,7 @@ public FilterChainHolder getFilterChain(HttpServletRequest request) {
130130
* @param targetPath The request path - this is extracted with the <code>getPath</code> method of the request object
131131
* @return A populated FilterChainHolder
132132
*/
133-
protected FilterChainHolder getFilterChainCache(DispatcherType type, String targetPath) {
133+
protected FilterChainHolder getFilterChainCache(final DispatcherType type, final String targetPath) {
134134
TargetCacheKey key = new TargetCacheKey();
135135
key.setDispatcherType(type);
136136
key.setTargetPath(targetPath);
@@ -147,7 +147,7 @@ protected FilterChainHolder getFilterChainCache(DispatcherType type, String targ
147147
* @param targetPath The target path in the API
148148
* @param holder The FilterChainHolder object to save in the cache
149149
*/
150-
protected void putFilterChainCache(DispatcherType type, String targetPath, FilterChainHolder holder) {
150+
protected void putFilterChainCache(final DispatcherType type, final String targetPath, final FilterChainHolder holder) {
151151
TargetCacheKey key = new TargetCacheKey();
152152
key.setDispatcherType(type);
153153
key.setTargetPath(targetPath);
@@ -167,7 +167,7 @@ protected void putFilterChainCache(DispatcherType type, String targetPath, Filte
167167
* @param mapping The mapping path stored in the filter registration
168168
* @return true if the given mapping path can apply to the target, false otherwise.
169169
*/
170-
protected boolean pathMatches(String target, String mapping) {
170+
protected boolean pathMatches(final String target, final String mapping) {
171171
// easiest case, they are exactly the same
172172
if (target.toLowerCase().equals(mapping.toLowerCase())) {
173173
return true;

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/FilterHolder.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ public class FilterHolder {
2828
// Variables - Private
2929
//-------------------------------------------------------------
3030

31-
private Filter filter;
32-
private FilterConfig filterConfig;
33-
private Registration registration;
31+
private Filter filter;
32+
private FilterConfig filterConfig;
33+
private Registration registration;
3434
private String filterName;
3535
private Map<String, String> initParameters;
3636

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/testutils/AwsProxyRequestBuilder.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
import com.amazonaws.serverless.proxy.internal.model.ApiGatewayRequestContext;
1717
import com.amazonaws.serverless.proxy.internal.model.ApiGatewayRequestIdentity;
1818
import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest;
19+
import com.fasterxml.jackson.core.JsonProcessingException;
20+
import com.fasterxml.jackson.databind.ObjectMapper;
1921

2022
import javax.ws.rs.core.HttpHeaders;
2123
import javax.ws.rs.core.MediaType;
@@ -31,6 +33,7 @@ public class AwsProxyRequestBuilder {
3133
//-------------------------------------------------------------
3234

3335
private AwsProxyRequest request;
36+
private ObjectMapper mapper;
3437

3538

3639
//-------------------------------------------------------------
@@ -48,6 +51,8 @@ public AwsProxyRequestBuilder(String path) {
4851

4952

5053
public AwsProxyRequestBuilder(String path, String httpMethod) {
54+
this.mapper = new ObjectMapper();
55+
5156
this.request = new AwsProxyRequest();
5257
this.request.setHttpMethod(httpMethod);
5358
this.request.setPath(path);
@@ -125,6 +130,18 @@ public AwsProxyRequestBuilder body(String body) {
125130
return this;
126131
}
127132

133+
public AwsProxyRequestBuilder body(Object body) {
134+
if (request.getHeaders() != null && request.getHeaders().get(HttpHeaders.CONTENT_TYPE).equals(MediaType.APPLICATION_JSON)) {
135+
try {
136+
return body(mapper.writeValueAsString(body));
137+
} catch (JsonProcessingException e) {
138+
throw new UnsupportedOperationException("Could not serialize object: " + e.getMessage());
139+
}
140+
} else {
141+
throw new UnsupportedOperationException("Unsupported content type in request");
142+
}
143+
}
144+
128145

129146
public AwsProxyRequestBuilder authorizerPrincipal(String principal) {
130147
if (this.request.getRequestContext().getAuthorizer() == null) {

aws-serverless-java-container-spring/pom.xml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,27 @@
7878
<version>${jackson.version}</version>
7979
<scope>test</scope>
8080
</dependency>
81+
82+
<dependency>
83+
<groupId>org.hibernate</groupId>
84+
<artifactId>hibernate-validator</artifactId>
85+
<version>5.4.1.Final</version>
86+
<scope>test</scope>
87+
</dependency>
88+
89+
<dependency>
90+
<groupId>javax.el</groupId>
91+
<artifactId>javax.el-api</artifactId>
92+
<version>2.2.4</version>
93+
<scope>test</scope>
94+
</dependency>
95+
96+
<dependency>
97+
<groupId>org.glassfish.web</groupId>
98+
<artifactId>javax.el</artifactId>
99+
<version>2.2.4</version>
100+
<scope>test</scope>
101+
</dependency>
81102
</dependencies>
82103

83104
<build>

aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/SpringServletContextTest.java

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,14 @@
99
import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
1010
import com.amazonaws.serverless.proxy.spring.echoapp.CustomHeaderFilter;
1111
import com.amazonaws.serverless.proxy.spring.echoapp.EchoSpringAppConfig;
12-
import com.amazonaws.serverless.proxy.spring.echoapp.model.MapResponseModel;
13-
import com.amazonaws.serverless.proxy.spring.echoapp.model.SingleValueModel;
14-
import com.fasterxml.jackson.core.JsonProcessingException;
15-
import com.fasterxml.jackson.databind.ObjectMapper;
16-
import org.apache.commons.codec.binary.Base64;
12+
import com.amazonaws.serverless.proxy.spring.echoapp.model.ValidatedUserModel;
1713
import org.junit.BeforeClass;
1814
import org.junit.Test;
19-
import org.junit.runner.RunWith;
20-
import org.springframework.beans.factory.annotation.Autowired;
21-
import org.springframework.test.annotation.DirtiesContext;
22-
import org.springframework.test.context.ContextConfiguration;
23-
import org.springframework.test.context.TestExecutionListeners;
24-
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
25-
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
26-
import org.springframework.test.context.web.WebAppConfiguration;
15+
import org.springframework.http.HttpStatus;
2716

2817
import javax.servlet.DispatcherType;
2918
import javax.servlet.FilterRegistration;
30-
import javax.ws.rs.core.HttpHeaders;
31-
import javax.ws.rs.core.MediaType;
32-
import java.io.IOException;
3319
import java.util.EnumSet;
34-
import java.util.UUID;
3520

3621
import static org.junit.Assert.*;
3722

@@ -98,5 +83,18 @@ public void filter_customHeaderFilter_echoHeaders() {
9883
assertNotNull(output.getHeaders().get(CustomHeaderFilter.HEADER_NAME));
9984
assertEquals(CustomHeaderFilter.HEADER_VALUE, output.getHeaders().get(CustomHeaderFilter.HEADER_NAME));
10085
}
86+
87+
@Test
88+
public void filter_validationFilter_emptyName() {
89+
ValidatedUserModel userModel = new ValidatedUserModel();
90+
userModel.setFirstName("Test");
91+
AwsProxyRequest request = new AwsProxyRequestBuilder("/context/user", "POST")
92+
.json()
93+
.body(userModel)
94+
.build();
95+
96+
AwsProxyResponse output = handler.proxy(request, lambdaContext);
97+
assertEquals(HttpStatus.BAD_REQUEST.value(), output.getStatusCode());
98+
}
10199
}
102100

aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/ContextResource.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
package com.amazonaws.serverless.proxy.spring.echoapp;
22

3+
import com.amazonaws.serverless.proxy.spring.echoapp.model.ValidatedUserModel;
34
import org.springframework.http.HttpStatus;
45
import org.springframework.http.ResponseEntity;
6+
import org.springframework.validation.BindingResult;
7+
import org.springframework.web.bind.annotation.RequestBody;
58
import org.springframework.web.bind.annotation.RequestMapping;
69
import org.springframework.web.bind.annotation.RequestMethod;
710
import org.springframework.web.bind.annotation.RestController;
811
import org.springframework.web.context.ServletContextAware;
912
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
1013

1114
import javax.servlet.ServletContext;
15+
import javax.validation.Valid;
16+
import javax.ws.rs.core.MediaType;
1217

1318
@RestController
1419
@EnableWebMvc
@@ -21,6 +26,16 @@ public ResponseEntity<String> getContext() {
2126
return new ResponseEntity<String>(this.context.getServerInfo(), HttpStatus.OK);
2227
}
2328

29+
@RequestMapping(path = "/user", method=RequestMethod.POST, consumes = MediaType.APPLICATION_JSON)
30+
public ResponseEntity<ValidatedUserModel> createUser(@Valid @RequestBody ValidatedUserModel newUser, BindingResult results) {
31+
32+
if (results.hasErrors()) {
33+
System.out.println("Has errors");
34+
return new ResponseEntity<ValidatedUserModel>(newUser, HttpStatus.BAD_REQUEST);
35+
}
36+
return new ResponseEntity<ValidatedUserModel>(newUser, HttpStatus.OK);
37+
}
38+
2439
@Override
2540
public void setServletContext(ServletContext servletContext) {
2641
context = servletContext;

aws-serverless-java-container-spring/src/test/java/com/amazonaws/serverless/proxy/spring/echoapp/EchoSpringAppConfig.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22

33
import com.amazonaws.serverless.exceptions.ContainerInitializationException;
44
import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
5-
import com.amazonaws.serverless.proxy.spring.LambdaSpringApplicationInitializer;
65
import com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler;
7-
import com.amazonaws.services.lambda.runtime.Context;
86
import com.fasterxml.jackson.databind.ObjectMapper;
97
import org.springframework.beans.factory.annotation.Autowired;
108
import org.springframework.context.annotation.Bean;
119
import org.springframework.context.annotation.ComponentScan;
1210
import org.springframework.context.annotation.Configuration;
11+
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
1312
import org.springframework.web.context.ConfigurableWebApplicationContext;
1413

1514
@Configuration
@@ -35,4 +34,9 @@ public ObjectMapper objectMapper() {
3534
public MockLambdaContext lambdaContext() {
3635
return new MockLambdaContext();
3736
}
37+
38+
@Bean
39+
public javax.validation.Validator localValidatorFactoryBean() {
40+
return new LocalValidatorFactoryBean();
41+
}
3842
}

0 commit comments

Comments
 (0)