Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: start context in MicronautRequestStreamHandler #1985

Merged
merged 1 commit into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,9 @@ public class MicronautRequestStreamHandler extends StreamFunctionExecutor<Contex
* Lambda deployment.
*/
public MicronautRequestStreamHandler() {
// initialize the application context in the constructor
// this is faster in Lambda as init cost is giving higher processor priority
// see https://github.com/micronaut-projects/micronaut-aws/issues/18#issuecomment-530903419
try {
buildApplicationContext(null);
} catch (Exception e) {
LOG.error("Exception initializing handler: " + e.getMessage(), e);
throw e;
}
buildApplicationContext(null);
startEnvironment(applicationContext);
injectIntoApplicationContext();
}

/**
Expand All @@ -72,6 +66,12 @@ public MicronautRequestStreamHandler() {
*/
public MicronautRequestStreamHandler(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
startEnvironment(applicationContext);
injectIntoApplicationContext();
}

private void injectIntoApplicationContext() {
applicationContext.inject(this);
}

@Override
Expand All @@ -91,7 +91,12 @@ public void handleRequest(InputStream input, OutputStream output, Context contex

@Override
protected ApplicationContext buildApplicationContext(Context context) {
return super.buildApplicationContext(context);
try {
return super.buildApplicationContext(context);
} catch (Exception e) {
LOG.error("Exception initializing handler: " + e.getMessage(), e);
throw e;
}
}

@NonNull
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package io.micronaut.function.aws

import com.amazonaws.services.lambda.runtime.ClientContext
import com.amazonaws.services.lambda.runtime.CognitoIdentity
import com.amazonaws.services.lambda.runtime.LambdaLogger
import com.amazonaws.services.lambda.runtime.Context

final class ContextUtils {
private ContextUtils() {
}

static Context mock() {
new Context() {
@Override
String getAwsRequestId() {
null
}

@Override
String getLogGroupName() {
null
}

@Override
String getLogStreamName() {
null
}

@Override
String getFunctionName() {
return "foo";
}

@Override
String getFunctionVersion() {
null
}

@Override
String getInvokedFunctionArn() {
null
}

@Override
CognitoIdentity getIdentity() {
null
}

@Override
ClientContext getClientContext() {
null
}

@Override
int getRemainingTimeInMillis() {
return 0;
}

@Override
int getMemoryLimitInMB() {
return 0;
}

@Override
LambdaLogger getLogger() {
null
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package io.micronaut.function.aws

import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent
import io.micronaut.context.ApplicationContext
import io.micronaut.context.env.Environment
import io.micronaut.function.FunctionBean
import io.micronaut.http.HttpMethod
import io.micronaut.json.JsonMapper
import jakarta.inject.Inject
import jakarta.inject.Singleton
import spock.lang.Specification

import java.util.function.Function

class MicronautRequestStreamHandlerStartsContextSpec extends Specification {

void "MicronautRequestStreamHandler starts application context"() throws IOException {
given:
String expectation = '{"message":"Hello World"}'
FunctionRequestHandler handler = new FunctionRequestHandler()

when:
JsonMapper jsonMapper = handler.getApplicationContext().getBean(JsonMapper.class)
APIGatewayProxyRequestEvent request = createRequest(HttpMethod.GET, "/")
APIGatewayProxyResponseEvent response = execute(handler, jsonMapper, request)

then: 'application context is started'
200 == response.getStatusCode().intValue()
expectation == response.body
and: "bean injection works in class extending MicronautRequestStreamHandler"
"HELLO WORLD" == handler.getUppercaser().uppercase("Hello World")

when:
handler.close()
handler = new FunctionRequestHandler(ApplicationContext.builder().build().stop())
jsonMapper = handler.getApplicationContext().getBean(JsonMapper.class)
request = createRequest(HttpMethod.GET, "/")
response = execute(handler, jsonMapper, request)

then: 'application context is started'
200 == response.getStatusCode().intValue()
expectation == response.body

and: "bean injection works in class extending MicronautRequestStreamHandler"
"HELLO WORLD" == handler.getUppercaser().uppercase("Hello World")

cleanup:
handler.close()
}

private static APIGatewayProxyRequestEvent createRequest(HttpMethod method, String path) {
APIGatewayProxyRequestEvent request = new APIGatewayProxyRequestEvent();
request.setHttpMethod(method.name());
request.setPath(path);
return request;
}

private static APIGatewayProxyResponseEvent execute(MicronautRequestStreamHandler handler,
JsonMapper jsonMapper,
APIGatewayProxyRequestEvent request) throws IOException {
ByteArrayInputStream inputStream = new ByteArrayInputStream(jsonMapper.writeValueAsBytes(request))
ByteArrayOutputStream output = new ByteArrayOutputStream()
handler.handleRequest(inputStream, output, ContextUtils.mock())
return jsonMapper.readValue(output.toByteArray(), APIGatewayProxyResponseEvent.class)
}

static class FunctionRequestHandler extends MicronautRequestStreamHandler {
public static final String FUNCTION_NAME = "apiHandler"

@Inject
UpperCaseString uppercaser

FunctionRequestHandler() {
}

FunctionRequestHandler(ApplicationContext applicationContext) {
super(applicationContext)
}

UpperCaseString getUppercaser() {
return uppercaser
}

@Override
protected String resolveFunctionName(Environment env) {
return FUNCTION_NAME
}
}

@FunctionBean(FunctionRequestHandler.FUNCTION_NAME)
static class ApiHandler implements Function<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
@Inject
JsonMapper jsonMapper

@Override
APIGatewayProxyResponseEvent apply(APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent) {
APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent()
try {
String json = new String(jsonMapper.writeValueAsBytes([message: "Hello World"]))
response.setStatusCode(200)
response.setBody(json)
} catch (IOException e) {
response.setStatusCode(500)
}
return response;
}
}

@Singleton
static class UpperCaseString {
String uppercase(String str) {
str.toUpperCase()
}
}

}
Loading