Skip to content

Commit 1bbaf9b

Browse files
authored
Merge pull request #114 from networknt/issue111
fixes #111 create cache-explorer handler for native lambda
2 parents ea6c6ed + d83c8d9 commit 1bbaf9b

File tree

11 files changed

+188
-24
lines changed

11 files changed

+188
-24
lines changed

src/main/java/com/networknt/aws/lambda/app/LambdaApp.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@
99
import com.networknt.aws.lambda.handler.chain.Chain;
1010
import com.networknt.config.Config;
1111
import com.networknt.utility.ModuleRegistry;
12+
import com.networknt.utility.PathTemplateMatcher;
1213
import org.slf4j.Logger;
1314
import org.slf4j.LoggerFactory;
1415

16+
import java.util.HashMap;
17+
import java.util.Map;
18+
1519

1620
/**
1721
* This is the entry point for the middleware Lambda function that is responsible for cross-cutting concerns for the business Lambda
@@ -24,6 +28,7 @@ public class LambdaApp implements RequestHandler<APIGatewayProxyRequestEvent, AP
2428

2529
private static final Logger LOG = LoggerFactory.getLogger(LambdaApp.class);
2630
public static final LambdaAppConfig CONFIG = (LambdaAppConfig) Config.getInstance().getJsonObjectConfig(LambdaAppConfig.CONFIG_NAME, LambdaAppConfig.class);
31+
static final Map<String, PathTemplateMatcher<String>> methodToMatcherMap = new HashMap<>();
2732

2833
public LambdaApp() {
2934
if (LOG.isInfoEnabled()) LOG.info("LambdaApp is constructed");
@@ -42,7 +47,7 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv
4247
var requestPath = apiGatewayProxyRequestEvent.getPath();
4348
var requestMethod = apiGatewayProxyRequestEvent.getHttpMethod();
4449
LOG.debug("Request path: {} -- Request method: {}", requestPath, requestMethod);
45-
Chain chain = Handler.getChain(requestPath + "@" + requestMethod.toLowerCase());
50+
Chain chain = Handler.getChain(apiGatewayProxyRequestEvent);
4651
if(chain == null) chain = Handler.getDefaultChain();
4752
final var exchange = new LightLambdaExchange(context, chain);
4853
exchange.setInitialRequest(apiGatewayProxyRequestEvent);
@@ -51,5 +56,4 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv
5156
LOG.debug("Lambda CCC --end with response: {}", response);
5257
return response;
5358
}
54-
5559
}

src/main/java/com/networknt/aws/lambda/app/LambdaStreamApp.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.io.InputStream;
1717
import java.io.OutputStream;
1818
import java.nio.charset.StandardCharsets;
19+
import java.util.Locale;
1920

2021
/**
2122
* This is the entry point for the stream lambda function that is responsible for cross-cutting concerns for the business Lambda
@@ -50,7 +51,7 @@ public void handleRequest(InputStream inputStream, OutputStream outputStream, Co
5051
var requestPath = apiGatewayProxyRequestEvent.getPath();
5152
var requestMethod = apiGatewayProxyRequestEvent.getHttpMethod();
5253
LOG.debug("Request path: {} -- Request method: {}", requestPath, requestMethod);
53-
Chain chain = Handler.getChain(requestPath + "@" + requestMethod);
54+
Chain chain = Handler.getChain(apiGatewayProxyRequestEvent);
5455
if(chain == null) chain = Handler.getDefaultChain();
5556
final var exchange = new LightLambdaExchange(context, chain);
5657
exchange.setInitialRequest(apiGatewayProxyRequestEvent);

src/main/java/com/networknt/aws/lambda/handler/Handler.java

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
package com.networknt.aws.lambda.handler;
22

3+
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
34
import com.networknt.aws.lambda.handler.chain.Chain;
45
import com.networknt.config.Config;
56
import com.networknt.handler.config.EndpointSource;
67
import com.networknt.handler.config.HandlerConfig;
78
import com.networknt.handler.config.PathChain;
89
import com.networknt.utility.ModuleRegistry;
10+
import com.networknt.utility.PathTemplateMatcher;
911
import com.networknt.utility.Tuple;
1012
import org.slf4j.Logger;
1113
import org.slf4j.LoggerFactory;
1214

1315
import java.lang.reflect.InvocationTargetException;
1416
import java.util.*;
1517

18+
import static io.undertow.util.PathTemplateMatch.ATTACHMENT_KEY;
19+
1620
public class Handler {
1721

1822

@@ -22,9 +26,8 @@ public class Handler {
2226
// handlers defined in the handlers section. each handler keyed by a name.
2327
static final Map<String, LambdaHandler> handlers = new HashMap<>();
2428
// chain name to list of handlers mapping
25-
static final Map<String, List<LambdaHandler>> handlerListById = new HashMap<>();
26-
// endpoint to the Chain mapping
27-
static final Map<String, Chain> endpointChain = new HashMap<>();
29+
static final Map<String, Chain> handlerListById = new HashMap<>();
30+
static final Map<String, PathTemplateMatcher<String>> methodToMatcherMap = new HashMap<>();
2831
static Chain defaultChain;
2932

3033
public static void init() {
@@ -60,14 +63,13 @@ static void initChains() {
6063
// add the chains to the handler list by id list.
6164
for (var chainName : config.getChains().keySet()) {
6265
var chain = config.getChains().get(chainName);
63-
var handlerChain = new ArrayList<LambdaHandler>();
64-
66+
Chain handlerChain = new Chain(false);
6567
for (var chainItemName : chain) {
6668
var chainItem = handlers.get(chainItemName);
67-
68-
if (chainItem == null)
69-
throw new RuntimeException("Chain " + chainName + " uses Unknown handler: " + chainItemName);
70-
handlerChain.add(chainItem);
69+
if (chainItem == null) {
70+
throw new RuntimeException("Unknown handler in chain: " + chainItemName);
71+
}
72+
handlerChain.addChainable(chainItem);
7173
}
7274
handlerListById.put(chainName, handlerChain);
7375
}
@@ -135,10 +137,23 @@ private static void addSourceChain(PathChain sourceChain) {
135137
*/
136138
private static void addPathChain(PathChain pathChain) {
137139
var method = pathChain.getMethod();
140+
// Use a random integer as the id for a given path.
141+
int randInt = new Random().nextInt();
142+
143+
while (handlerListById.containsKey(Integer.toString(randInt)))
144+
randInt = new Random().nextInt();
138145

139146
// Flatten out the execution list from a mix of middleware chains and handlers.
140147
var handlers = getHandlersFromExecList(pathChain.getExec());
141-
endpointChain.put(pathChain.getPath() + "@" + pathChain.getMethod(), handlers);
148+
149+
PathTemplateMatcher<String> pathTemplateMatcher = methodToMatcherMap.containsKey(method)
150+
? methodToMatcherMap.get(method)
151+
: new PathTemplateMatcher<>();
152+
if (pathTemplateMatcher.get(pathChain.getPath()) == null)
153+
pathTemplateMatcher.add(pathChain.getPath(), Integer.toString(randInt));
154+
155+
methodToMatcherMap.put(method, pathTemplateMatcher);
156+
handlerListById.put(Integer.toString(randInt), handlers);
142157
}
143158

144159
/**
@@ -154,9 +169,9 @@ private static Chain getHandlersFromExecList(List<String> execs) {
154169
if (execs != null) {
155170

156171
for (var exec : execs) {
157-
var handlerList = handlerListById.get(exec);
172+
var handlerChain = handlerListById.get(exec);
158173

159-
if (handlerList == null) {
174+
if (handlerChain == null) {
160175
// not a chain, try to resolve it as a handler
161176
LambdaHandler handler = handlers.get(exec);
162177
if (handler != null) {
@@ -165,7 +180,7 @@ private static Chain getHandlersFromExecList(List<String> execs) {
165180
throw new RuntimeException("Unknown handler or chain: " + exec);
166181
}
167182
} else {
168-
for (LambdaHandler handler : handlerList) {
183+
for (LambdaHandler handler : handlerChain.getChain()) {
169184
if (handler.isEnabled())
170185
handlersFromExecList.addChainable(handler);
171186
}
@@ -195,7 +210,7 @@ private static void registerLambdaHandler(Object handler) {
195210
* constructor fields. To note: It could either implement HttpHandler, or
196211
* HandlerProvider.
197212
*
198-
* @param handler
213+
* @param handler handler string
199214
*/
200215
private static void initStringDefinedHandler(String handler) {
201216

@@ -264,9 +279,29 @@ public static Map<String, LambdaHandler> getHandlers() {
264279
return handlers;
265280
}
266281

267-
public static Chain getChain(String endpoint) {
268-
return endpointChain.get(endpoint);
282+
public static Chain getChain(APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent) {
283+
var requestPath = apiGatewayProxyRequestEvent.getPath();
284+
var requestMethod = apiGatewayProxyRequestEvent.getHttpMethod();
285+
// Get the matcher corresponding to the current request type.
286+
var pathTemplateMatcher = methodToMatcherMap.get(requestMethod.toLowerCase());
287+
if (pathTemplateMatcher != null) {
288+
// Match the current request path to the configured paths.
289+
var result = pathTemplateMatcher.match(requestPath);
290+
if (result != null) {
291+
// inject the path and query parameters into the request.
292+
for (var entry : result.getParameters().entrySet()) {
293+
// the values shouldn't be added to query param. but this is left as it was to keep backward compatability
294+
apiGatewayProxyRequestEvent.getQueryStringParameters().put(entry.getKey(), entry.getValue());
295+
// put values in path param map
296+
apiGatewayProxyRequestEvent.getPathParameters().put(entry.getKey(), entry.getValue());
297+
}
298+
var id = result.getValue();
299+
return handlerListById.get(id);
300+
}
301+
}
302+
return null;
269303
}
304+
270305
public static Chain getDefaultChain() {
271306
return defaultChain;
272307
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.networknt.aws.lambda.handler.cache;
2+
3+
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
4+
import com.networknt.aws.lambda.LightLambdaExchange;
5+
import com.networknt.aws.lambda.handler.LambdaHandler;
6+
import com.networknt.cache.CacheManager;
7+
import com.networknt.config.JsonMapper;
8+
import com.networknt.service.SingletonServiceFactory;
9+
import com.networknt.status.Status;
10+
import com.networknt.utility.ModuleRegistry;
11+
import org.slf4j.Logger;
12+
import org.slf4j.LoggerFactory;
13+
14+
import java.util.HashMap;
15+
import java.util.Map;
16+
17+
public class CacheExplorerHandler implements LambdaHandler {
18+
static final Logger logger = LoggerFactory.getLogger(CacheExplorerHandler.class);
19+
public static final String CACHE_NAME = "name";
20+
public static final String JWK = "jwk";
21+
public static final String OBJECT_NOT_FOUND = "ERR11637";
22+
23+
public CacheExplorerHandler() {
24+
logger.info("CacheExplorerHandler is constructed");
25+
}
26+
27+
@Override
28+
public Status execute(LightLambdaExchange exchange) {
29+
if (logger.isTraceEnabled()) logger.trace("CacheExplorerHandler.execute starts.");
30+
Map<String, String> headers = Map.of("Content-Type", "application/json");
31+
String name = exchange.getRequest().getPathParameters().get(CACHE_NAME);
32+
CacheManager cacheManager = SingletonServiceFactory.getBean(CacheManager.class);
33+
if(cacheManager != null) {
34+
Map<Object, Object> cacheMap = cacheManager.getCache(name);
35+
if(name.equals(JWK)) {
36+
Map<String, String> map = new HashMap<>();
37+
cacheMap.forEach((k, v) -> {
38+
map.put((String)k, v.toString());
39+
});
40+
var res = new APIGatewayProxyResponseEvent()
41+
.withStatusCode(200)
42+
.withHeaders(headers)
43+
.withBody(JsonMapper.toJson((map)));
44+
exchange.setInitialResponse(res);
45+
} else {
46+
var res = new APIGatewayProxyResponseEvent()
47+
.withStatusCode(200)
48+
.withHeaders(headers)
49+
.withBody(JsonMapper.toJson((cacheMap)));
50+
exchange.setInitialResponse(res);
51+
}
52+
} else {
53+
Status status = new Status(OBJECT_NOT_FOUND, "cache", name);
54+
var res = new APIGatewayProxyResponseEvent()
55+
.withStatusCode(status.getStatusCode())
56+
.withHeaders(headers)
57+
.withBody(status.toString());
58+
exchange.setInitialResponse(res);
59+
}
60+
if (logger.isTraceEnabled()) logger.trace("CacheExplorerHandler.execute ends.");
61+
return this.successMiddlewareStatus();
62+
}
63+
64+
@Override
65+
public boolean isEnabled() {
66+
return true;
67+
}
68+
69+
@Override
70+
public void register() {
71+
ModuleRegistry.registerModule(
72+
null,
73+
CacheExplorerHandler.class.getName(),
74+
null,
75+
null);
76+
}
77+
78+
@Override
79+
public void reload() {
80+
81+
}
82+
83+
@Override
84+
public boolean isAsynchronous() {
85+
return false;
86+
}
87+
88+
}

src/main/resources/config/handler.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ paths:
102102
- admin
103103
- setLogger
104104

105+
- path: '/adm/cache/{name}'
106+
method: 'get'
107+
exec:
108+
- admin
109+
- cache
110+
105111
# - path: '/adm/modules'
106112
# method: 'get'
107113
# exec:
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.networknt.aws.lambda.admin;
2+
3+
import com.amazonaws.services.lambda.runtime.Context;
4+
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
5+
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
6+
import com.networknt.aws.lambda.LambdaContext;
7+
import com.networknt.aws.lambda.LightLambdaExchange;
8+
import com.networknt.aws.lambda.TestUtils;
9+
import com.networknt.aws.lambda.app.LambdaApp;
10+
import com.networknt.aws.lambda.handler.Handler;
11+
import com.networknt.aws.lambda.handler.chain.Chain;
12+
import org.junit.jupiter.api.Test;
13+
14+
public class CacheExplorerHandlerTest {
15+
@Test
16+
public void testGetCacheJwk() {
17+
APIGatewayProxyRequestEvent requestEvent = TestUtils.createTestRequestEvent();
18+
requestEvent.setPath("/adm/cache/jwk");
19+
requestEvent.setHttpMethod("GET");
20+
LambdaApp lambdaApp = new LambdaApp();
21+
Chain chain = Handler.getChain(requestEvent);
22+
Context lambdaContext = new LambdaContext("1");
23+
LightLambdaExchange exchange = new LightLambdaExchange(lambdaContext, chain);
24+
exchange.setInitialRequest(requestEvent);
25+
APIGatewayProxyResponseEvent responseEvent = lambdaApp.handleRequest(requestEvent, lambdaContext);
26+
System.out.println(responseEvent.toString());
27+
}
28+
29+
}

src/test/java/com/networknt/aws/lambda/admin/HealthCheckHandlerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ void testHealthCheck() {
1818
requestEvent.setPath("/health");
1919
requestEvent.setHttpMethod("GET");
2020
LambdaApp lambdaApp = new LambdaApp();
21-
Chain chain = Handler.getChain("/adm/logger@get");
21+
Chain chain = Handler.getChain(requestEvent);
2222
Context lambdaContext = new LambdaContext("1");
2323
LightLambdaExchange exchange = new LightLambdaExchange(lambdaContext, chain);
2424
exchange.setInitialRequest(requestEvent);

src/test/java/com/networknt/aws/lambda/admin/LoggerHandlerTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ void testGetLogger() {
1818
requestEvent.setPath("/adm/logger");
1919
requestEvent.setHttpMethod("GET");
2020
LambdaApp lambdaApp = new LambdaApp();
21-
Chain chain = Handler.getChain("/adm/logger@get");
21+
Chain chain = Handler.getChain(requestEvent);
2222
Context lambdaContext = new LambdaContext("1");
2323
LightLambdaExchange exchange = new LightLambdaExchange(lambdaContext, chain);
2424
exchange.setInitialRequest(requestEvent);
@@ -34,7 +34,7 @@ void testSetLogger() {
3434
requestEvent.setHttpMethod("POST");
3535
requestEvent.setBody("[{\"name\":\"ROOT\",\"level\":\"INFO\"},{\"name\":\"com.networknt\",\"level\":\"INFO\"}]");
3636
LambdaApp lambdaApp = new LambdaApp();
37-
Chain chain = Handler.getChain("/adm/logger@post");
37+
Chain chain = Handler.getChain(requestEvent);
3838
Context lambdaContext = new LambdaContext("1");
3939
LightLambdaExchange exchange = new LightLambdaExchange(lambdaContext, chain);
4040
exchange.setInitialRequest(requestEvent);

src/test/java/com/networknt/aws/lambda/admin/ServerInfoHandlerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public void testServerInfo() {
1818
requestEvent.setPath("/adm/server/info");
1919
requestEvent.setHttpMethod("GET");
2020
LambdaApp lambdaApp = new LambdaApp();
21-
Chain chain = Handler.getChain("/adm/server/info@get");
21+
Chain chain = Handler.getChain(requestEvent);
2222
Context lambdaContext = new LambdaContext("1");
2323
LightLambdaExchange exchange = new LightLambdaExchange(lambdaContext, chain);
2424
exchange.setInitialRequest(requestEvent);

src/test/java/com/networknt/aws/lambda/handler/HandlerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ public class HandlerTest {
88
@Test
99
public void testInitHandler() {
1010
Handler.init();
11-
Assertions.assertEquals(24, Handler.getHandlers().size());
11+
Assertions.assertEquals(25, Handler.getHandlers().size());
1212
}
1313
}

0 commit comments

Comments
 (0)