private CompletableFuture<InvokeResponse> callLambda(InvocationType type, IN input) { log.debug("calling '{}' as {} with payload: {}", functionName, type, input); ByteBuffer payload = serialize(input); InvokeRequest request = InvokeRequest.builder() .functionName(functionName) .invocationType(type) .payload(payload) .build(); return lambda.invoke(request); }
@SneakyThrows private OUT deserialize(InvokeResponse response) { return reader.readValue(new ByteBufferBackedInputStream(response.payload())); } }
@Override public CompletableFuture<Void> triggerAsync(IN input) { CompletableFuture<InvokeResponse> response = callLambda(InvocationType.Event, input); return response.thenAccept( r -> log.debug("response from {}: {}", functionName, r.statusCode())); }
@Override @SneakyThrows public CompletableFuture<InvokeResponse> invoke(final InvokeRequest invokeRequest) { ByteBufferBackedInputStream inputStream = new ByteBufferBackedInputStream(invokeRequest.payload()); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); handler.handleRequest(inputStream, outputStream, null); return CompletableFuture.completedFuture( InvokeResponse.builder().payload(ByteBuffer.wrap(outputStream.toByteArray())).build()); } }
@Test public void callUsesRequestReply() { function.callAsync(new TestInput("test")).join(); verify(client).invoke(requestArgument.capture()); assertThat( requestArgument.getValue().invocationType(), equalTo(InvocationType.RequestResponse.toString())); }
@Test public void serializesInputsUsingMapper() throws Exception { TestOutput output = function.call(new TestInput("test")); verify(client).invoke(requestArgument.capture()); ByteBuffer actualPayload = requestArgument.getValue().payload(); assertThat( mapper .readValue(new ByteBufferBackedInputStream(actualPayload), TestInput.class) .getInput(), equalTo("test")); }
@Test public void passesCorrectFunctionName() { function.call(new TestInput("test")); verify(client).invoke(requestArgument.capture()); assertThat(requestArgument.getValue().functionName(), equalTo("TestFunction")); }
@Override public final Builder compatibleRuntime(Runtime compatibleRuntime) { this.compatibleRuntime(compatibleRuntime.toString()); return this; }
/** * Invoke a JSON-RPC method asynchronously without compile-time type-checking. * * <p>This overload is private, because it should only be used by the {@link * JsonRpcLambdaProxyAdapter} (which requires a non-typesafe invoke method). */ private CompletableFuture<Object> invokeAsync(String methodName, Object argument, Type returnType) throws IOException { ByteBuffer requestPayload = writeRequest(methodName, argument); InvokeRequest invokeRequest = InvokeRequest.builder().functionName(functionName).payload(requestPayload).build(); CompletableFuture<InvokeResponse> pendingRequest = lambda.invoke(invokeRequest); return pendingRequest.thenApply(r -> readResponse(returnType, r)); }
/** Deserialize a JSON-RPC response from a ByteBuffer from a Lambda response payload. */ private Object readResponse(Type returnType, InvokeResponse response) { try { log.trace( "Raw response payload: " + StandardCharsets.UTF_8.decode(response.payload()).toString()); return streamClient.readResponse( returnType, new ByteBufferBackedInputStream(response.payload())); } catch (Throwable t) { if (t instanceof IOException) { log.warn("Could not read JSON-RPC response: ", t); } else { // This exception is raised when deserialized from the remote call, and must be // handled/logged in calling code; so only log at DEBUG level log.debug("Read an exception value from JSON-RPC response: ", t); } // wrap the raw Throwable from the JSON RPC client so that this method can be used in // CompletableFuture::thenApply throw new CompletionException(t); } } }
@Test public void triggerUsesEventInvocation() { function.triggerAsync(new TestInput("test")).join(); verify(client).invoke(requestArgument.capture()); assertThat( requestArgument.getValue().invocationType(), equalTo(InvocationType.Event.toString())); }