Micronaut AWS

Provides integration between Micronaut and Amazon Web Services (AWS)

Version:

1 Introduction

This project provides various extensions to Micronaut for Amazon Web Services (AWS). The primary focus initially is on AWS Lambda, however other integrations may be included in this project in the future.

A base AWSClientConfiguration is provided which can be used as a base configuration class for any configuration that needs to configure an AWS SDK client.

2 What's new?

Micronatu AWS 2.0.0 contains improvements to ease the creation of Alexa Skills with Micronaut:

  • Creation of Alexa Skills as http services.

  • Creation of Flash Briefing skills.

  • Easier creation of SSML.

Check the Alexa section to learn more.

See the section Breaking Changes to ease into the migration to Micronaut AWS 2.x

3 Amazon Correto

Amazon Corretto is an OpenJDK distribution that provides free, long-term support with no pay-gated features or restrictions on how it’s used in production. Corretto is used by thousands of Amazon workloads; for example, it’s the JDK used by the AWS Lambda java11 runtime, which provides insights Amazon uses to push improvements upstream.

4 AWS Credentials Provider

When working with AWS SDK, you may need to provide a com.amazonaws.auth.AWSCredentialsProvider. To ease that this module provides a utility class: EnvironmentAWSCredentialsProvider.

For example the following snippet show how you may configure a S3 Client if you set two environment variables:

export AWS_ACCESS_KEY_ID=XXXX
export AWS_SECRET_KEY=YYYY
AmazonS3ClientBuilder amazonS3ClientBuilder = AmazonS3ClientBuilder.standard();
  amazonS3ClientBuilder.setCredentials(new EnvironmentAWSCredentialsProvider(applicationContext.getEnvironment()));
AmazonS3 s3 = amazonS3ClientBuilder.build();

5 AWS Lambda Support

Regular Micronaut functions created via mn create-function can be deployed to Lambda directly.

Using the CLI
$ mn create-function example

For example, when using create-function hello Micronaut 1.1 and above will create a function that looks like this:

package example;
import io.micronaut.function.executor.FunctionInitializer;
import javax.inject.*;

@Singleton
public class EchoFunction extends FunctionInitializer { (1)

    @Inject GreetingService greetingService; (2)

    public Message echo(Message msg) { (3)
        return greetingService.hello(msg);
    }

    public static void main(String...args) throws IOException { (4)
        EchoFunction function = new EchoFunction();
        function.run(args, (context)-> function.echo(context.get(Message.class)));
    }
}
1 The function should be a simple class annotated with @Singleton that extends from FunctionInitializer and has no constructor arguments.
2 You can use dependency injection to inject fields.
3 The function should be defined as a public method, and accept a single argument that is a simple java.lang type or a POJO bean that can be serialized/deserialized to JSON.
4 You can optionally define a main method if you plan to deploy the function to other FaaS environments that use standard in/out. If not, you can remove the main method

The FunctionInitiazer super class will bootstrap Micronaut and perform injection on the instance prior to the echo method being executed.

An appropriate SAM definition for this function would be:

Resources:
  HelloFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: example.EchoFunction::echo

The key part is the Handler which should be set to example.EchoFunction::echo which is the class name and method name combination. The main method is optional and can be removed if it is not needed. The main method allows the function to be executed from the cli, for example:

$ echo '{"name":"Fred"}' | java -jar function.jar

The above example pipes the JSON into system in which the functions reads and writes the result to system out. Certain FaaS systems such as OpenFaaS use system in/out for function inputs and outputs, hence with the main method in place you can deploy the function to such systems.

If you need further integration with what AWS Lambda has to offer, then this project includes a number of parent classes and utilities to simplify working with the native Lambda APIs.

5.1 Micronaut Request Handlers

The micronaut-function-aws module includes two parent classes you can subclass to implement native AWS Lambda functionality. To get started first add the micronaut-function-aws dependency to your build:

implementation("io.micronaut:micronaut-function-aws")
<dependency>
    <groupId>io.micronaut</groupId>
    <artifactId>micronaut-function-aws</artifactId>
</dependency>

You can then use either MicronautRequestHandler or MicronautRequestStreamHandler as your parent class to implement your Lambda handler. For example:

Implementing MicronautRequestHandler
package example;

import io.micronaut.context.env.Environment;
import javax.inject.Inject;

public class RoundHandler extends MicronautRequestHandler<Float, Integer> { (1)

    @Inject
    MathService mathService; (2)

    @Inject
    Environment env;

    @Override
    public Integer execute(Float input) {
        return mathService.round(input); (3)
    }
}
1 The class extends MicronautRequestHandler and must have a default constructor.
2 You can use field injection to inject dependencies
3 The function implementation

With MicronautRequestHandler it is expected that you supply generic types with the input and the output types.

If you wish to work with raw streams then subclass MicronautRequestStreamHandler instead.

You should then configure the class name as the Lambda handler when deploying to AWS. For example with SAM:

Resources:
  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: example.RoundHandler::handleRequest

5.2 AWS API Gateway Support

An alternative approach to using Micronaut with AWS is to use Micronaut support for the AWS Serverless Java Container project.

Using the CLI with Micronaut 1.1 or above
$ mn create-app my-app --features aws-api-gateway

In this arrangement you build your application as a regular REST application, for example:

Example Controller
package example;

import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Controller("/ping")
public class PingController {

	private Logger log = LoggerFactory.getLogger(PingController.class);

    @Get("/")
    public String ping() {
    	log.trace("Received a ping.");
    	return "{\"pong\":true}";
    }
}

You then need to add the micronaut-function-aws-api-proxy dependency:

implementation("io.micronaut.aws:micronaut-function-aws-api-proxy")
<dependency>
    <groupId>io.micronaut.aws</groupId>
    <artifactId>micronaut-function-aws-api-proxy</artifactId>
</dependency>

You can then implement a RequestHandler that handles the API proxy request:

Example Stream Lambda Handler
package example;

import com.amazonaws.serverless.exceptions.ContainerInitializationException;
import com.amazonaws.serverless.proxy.model.*;
import com.amazonaws.services.lambda.runtime.*;
import io.micronaut.function.aws.proxy.MicronautLambdaContainerHandler;
import java.io.*;

public class StreamLambdaHandler implements RequestStreamHandler {

    private MicronautLambdaContainerHandler handler;

    public StreamLambdaHandler() {
        try {
            handler = new MicronautLambdaContainerHandler(); (1)
        } catch (ContainerInitializationException e) {
            // if we fail here. We re-throw the exception to force another cold start
            e.printStackTrace();
            throw new RuntimeException("Could not initialize Micronaut", e);
        }
    }

    @Override
    public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
            throws IOException {
        handler.proxyStream(inputStream, outputStream, context); (2)
    }
}
1 Micronaut is initialized in the constructor
2 The API Proxy Request is handled

The MicronautLambdaContainerHandler class is used to initialization Micronaut and handle the serverless requests.

The MicronautLambdaContainerHandler constructor also accepts a ApplicationContextBuilder instance which you can use to further customize application bootstrap.

See the Example Application and associated README for instructions on how to deploy locally using AWS SAM Local and AWS Cloud Formation for production.

Micronaut API Gateway Proxy builds on top of AWS Servless Java Container. You can check a Pet Store sample application.

ObjectMapper configuration

By default, for efficiency reasons, Micronaut uses the default Jackson ObjectMapper to communicate with the AWS API proxy. However, if you have configured it for your application (for example, setting jackson.propertyNamingStrategy: SNAKE_CASE) in a way that it would be incompatible with the API proxy, you can set aws.proxy.shared-object-mapper: false, and Micronaut will create a brand new ObjectMapper for the API proxy.

If you wish to further configure this ObjectMapper, you can register a BeanCreatedEventListener<ObjectMapper> and filter based on event.getBeanDefinition() having an annotation like @Named("aws").

6 AWS Lambda Custom Runtime

Micronaut’s eases the deployment of your functions as a Custom AWS Lambda runtime:

A runtime is a program that runs a Lambda function’s handler method when the function is invoked. You can include a runtime in your function’s deployment package in the form of an executable file named bootstrap.

Why would you want to deploy your function as a Custom Runtime? For example, to use a different JVM than the one AWS provides or execute your function as GraalVM Native Image.

First, you need to add the micronaut-function-aws-custom-runtime dependency to your build:

implementation("io.micronaut.aws:micronaut-function-aws-custom-runtime")
<dependency>
    <groupId>io.micronaut.aws</groupId>
    <artifactId>micronaut-function-aws-custom-runtime</artifactId>
</dependency>

The main API you will interact with is AbstractMicronautLambdaRuntime. An abstract class which you can extend to create your custom runtime mainClass. That class includes the necessary code to perform the Processing Tasks described in the Custom Runtime documentation.

6.1 Custom Runtime for Handler functions

Lets describe a scenario with a handler which works with an input and output POJO.

package io.micronaut.docs;

import io.micronaut.core.annotation.Introspected;
import io.micronaut.function.aws.MicronautRequestHandler;

@Introspected (1)
class CustomMicronautRequestHandler extends MicronautRequestHandler<Book, BookSaved> {

    @Override
    public BookSaved execute(Book input) {
        BookSaved bookSaved = new BookSaved();
        bookSaved.setName(input.getName());
        bookSaved.setIsbn("XXX");
        return bookSaved;
    }
}
1 Annotate your handler with io.micronaut.core.annotation.Introspected so that Micronaut can instantiate the handler.
import edu.umd.cs.findbugs.annotations.NonNull;
import io.micronaut.core.annotation.Introspected;
import javax.validation.constraints.NotBlank;

@Introspected (1)
public class Book {

    @NonNull
    @NotBlank
    private String name;

    public Book() {
    }

    // Getters & Setters
1 Annotate your POJOs with io.micronaut.core.annotation.Introspected for reflection-free JSON serialization and deserialization.
import edu.umd.cs.findbugs.annotations.NonNull;
import io.micronaut.core.annotation.Introspected;
import javax.validation.constraints.NotBlank;

@Introspected (1)
public class BookSaved {

    @NonNull
    @NotBlank
    private String name;

    @NonNull
    @NotBlank
    private String isbn;

    public BookSaved() {

    }

    // Getters & Setters
1 Annotate your POJOs with io.micronaut.core.annotation.Introspected for reflection-free JSON serialization and deserialization.
In the AWS Console, as Handler value you will set io.micronaut.docs.CustomMicronautRequestHandler.

bootstrap

You will deploy your custom runtime code as a ZIP file. At the root of the ZIP file you need a bootstrap file.

If there’s a file named bootstrap in your deployment package, Lambda executes that file.

Create a bootstrap bash script:

Example bootstrap
#!/bin/sh
set -euo pipefail
java -XX:TieredStopAtLevel=1 -noverify -cp demo.jar io.micronaut.docs.MyLambdaRuntime

Create the class referenced in the previous bootstrap file:

package io.micronaut.docs;

import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import io.micronaut.function.aws.runtime.AbstractMicronautLambdaRuntime;

public class MyLambdaRuntime
        extends AbstractMicronautLambdaRuntime<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent, Book, BookSaved> {
}

The above class is an example of a Lambda whose trigger is an API Gateway event.

6.2 Deploy a Custom Runtime

You can deploy it via the AWS console or the AWS CLI. You will need to create a zip bundle including the bootstrap and your application JAR file (in the above case named demo.jar).

If you use the AWS CLI, specify a runtime of provided:

aws lambda create-function --function-name my-function \
--zip-file fileb://function.zip --handler function.handler --runtime provided \
--role arn:aws:iam::123456789:role/lambda_basic_execution
See Publishing a Custom Runtime for more information.

6.3 Custom runtime for AWS API Gateway Proxy functions

If you build your application with the API Gateway approach. You don’t need to create a class which extends AbstractMicronautLambdaRuntime you can use MicronautLambdaRuntime.

The bootstrap bash script will reference that class:

Example bootstrap for MicronautLambdaRuntime
#!/bin/sh
set -euo pipefail
java -XX:TieredStopAtLevel=1 -noverify -cp demo.jar io.micronaut.function.aws.runtime.MicronautLambdaRuntime

In addition to the dependencies to micronaut-function-aws-aws-custom-runtime, you will need to include dependencies to the micronaut-function-aws-api-proxy module.

6.4 Custom runtime for Function as a GraalVM Native Image

To achieve the absolutely best cold startup time you can create a function that is compiled into a GraalVM native image and then run it from the bootstrap script.

You need several pieces in place:

GraalVM native-image.properties

First you will need to create a native-image.properties file:

src/main/resources/META-INF/io/micronaut/docs/native-image.properties
Args = -H:IncludeResources=logback.xml|application.yml \
       -H:Name=demo (1)
       -H:Class=io.micronaut.docs.MyLambdaRuntime (2)
1 This is the native image name. You reference this name in the Dockerfile which builds the native image
2 This is the mainClass of your app.

A Dockerfile to build a GraalVM native image

Create a Dockerfile which:

  • Uses the amazonlinux image

  • Builds the JAR of the function.

  • Install the necessary dependencies.

  • Downloads GraalVM community edition

  • Installs native-image utility.

  • With the native-image command and the JAR, generates a GraalVM native image

  • Bundles the native image of our function and the bootstrap file into a ZIP file.

Dockerfile to generate a native image
FROM gradle:6.3.0-jdk8 as builder
COPY --chown=gradle:gradle . /home/application
WORKDIR /home/application
RUN ./gradlew build --no-daemon

FROM amazonlinux:2018.03.0.20191014.0 as graalvm

ENV LANG=en_US.UTF-8

RUN yum install -y gcc gcc-c++ libc6-dev  zlib1g-dev curl bash zlib zlib-devel zip

ENV GRAAL_VERSION 20.0.0
ENV JDK_VERSION java8
ENV GRAAL_FILENAME graalvm-ce-${JDK_VERSION}-linux-amd64-${GRAAL_VERSION}.tar.gz

RUN curl -4 -L https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-${GRAAL_VERSION}/${GRAAL_FILENAME} -o /tmp/${GRAAL_FILENAME}

RUN tar -zxvf /tmp/${GRAAL_FILENAME} -C /tmp \
    && mv /tmp/graalvm-ce-${JDK_VERSION}-${GRAAL_VERSION} /usr/lib/graalvm

RUN rm -rf /tmp/*
CMD ["/usr/lib/graalvm/bin/native-image"]

FROM graalvm
COPY --from=builder /home/application/ /home/application/
WORKDIR /home/application
RUN /usr/lib/graalvm/bin/gu install native-image
RUN /usr/lib/graalvm/bin/native-image --no-server -cp build/libs/demo-*-all.jar
RUN chmod 777 bootstrap
RUN chmod 777 demo (1)
RUN zip -j function.zip bootstrap demo (1)
EXPOSE 8080
ENTRYPOINT ["/home/application/demo"]
1 It matches the name used in the native-image.properties file.

Execute the native image from bootstrap

Invoke the native image from the bootstrap script:

Example bootstrap with native image
#!/bin/sh
set -euo pipefail
./demo -Xmx128m -Djava.library.path=$(pwd)

A script to build the function ZIP

You can then use Docker to build a function ZIP file ready for deployment to AWS Lambda:

deploy.sh
#!/bin/bash
docker build . -t demo
mkdir -p build
docker run --rm --entrypoint cat demo  /home/application/function.zip > build/function.zip

7 Alexa Support

The Micronaut’s aws-alexa module simplifies development of Alexa Skills with Java, Kotlin or Groovy.

implementation("io.micronaut.aws:micronaut-aws-alexa")
<dependency>
    <groupId>io.micronaut.aws</groupId>
    <artifactId>micronaut-aws-alexa</artifactId>
</dependency>

To create the sample skill described in Amazon Documentation - Develop your first skill with Micronaut’s Alexa you will write the same LaunchRequestHandler, HelloWorldIntent, HelpIntent, CancelandStopHandler, FallbackIntentHandler, SessionEndedRequestHandler handlers.

You will do just one change, you will annotate those handlers with javax.inject.Singleton.

import com.amazon.ask.dispatcher.request.handler.HandlerInput;
import com.amazon.ask.dispatcher.request.handler.RequestHandler;
import com.amazon.ask.model.Response;
import com.amazon.ask.request.Predicates;

import javax.inject.Singleton;
import java.util.Optional;

@Singleton (1)
public class HelloWorldIntentHandler implements RequestHandler {

    @Override
    public boolean canHandle(HandlerInput input) {
        return input.matches(Predicates.intentName("HelloWorldIntent"));
    }

    @Override
    public Optional<Response> handle(HandlerInput input) {
        String speechText = "Hello world";
        return input.getResponseBuilder()
                .withSpeech(speechText)
                .withSimpleCard("HelloWorld", speechText)
                .build();
    }

}
1 The Singleton scope indicates only one instance of the bean should exist in the Micronaut’s Bean Context

Typically, the next step will be to provide an instance of AlexaSkillConfiguration. The easiest way to do that is to configure the skill id via configuration:

alexa:
  skills:
    myskill:
      skill-id 'xxxx-yaaa-zz123'

Micronaut’s alexa module provides by default StandardSkillBuilderProvider which creates an SDK instance using the Skills.standard builder. You can provide your own implementation of Micronaut’s SkillBuilderProvider.

For each AlexaSkillConfiguration bean, Micronaut uses the builder provided by [SkillBuilderProvider to create for you a bean of type AlexaSkill for you and wires up the beans of the following types:

  • com.amazon.ask.dispatcher.request.handler.RequestHandler

  • com.amazon.ask.dispatcher.request.interceptor.RequestInterceptor

  • com.amazon.ask.dispatcher.request.interceptor.ResponseInterceptor

  • com.amazon.ask.dispatcher.exception.ExceptionHandler

  • com.amazon.ask.builder.SkillBuilder

7.1 SSML Builder

Micronaut Alexa ships with a Speech Systhesys Markup Language builder.

new Ssml().speak(new Ssml("Welcome to Ride Hailer. ").audio('soundbank://soundlibrary/transportation/amzn_sfx_car_accelerate_01').build()).build() == '<speak>Welcome to Ride Hailer. <audio src="soundbank://soundlibrary/transportation/amzn_sfx_car_accelerate_01"/></speak>'

7.2 The IntentHandler Annotation

To simplify the programming model Micronaut AWS includes a @IntentHandler annotation that can be used on any bean method to make the method an intent handler.

The method must accept a single value of type com.amazon.ask.dispatcher.request.handler.HandlerInput and return a value of type Optional<com.amazon.ask.model.Response> otherwise a compilation error will occur.

A typical Alexa application written in Micronaut looks like:

import com.amazon.ask.dispatcher.request.handler.HandlerInput;
import com.amazon.ask.model.Response;
import io.micronaut.function.aws.alexa.AlexaIntents;
import io.micronaut.function.aws.alexa.annotation.IntentHandler;

import javax.inject.Singleton;
import java.util.Optional;

@Singleton (1)
public class AlexaApplication {

    public static final String INTENT_NAME = "HelloWorldIntent";

    private final MessageService messageService;

    public AlexaApplication(MessageService messageService) { (2)
        this.messageService = messageService;
    }

    @IntentHandler(INTENT_NAME) (3)
    public Optional<Response> greet(HandlerInput input) { (4)
        String speechText = messageService.sayHello();
        return input.getResponseBuilder()
                .withSpeech(speechText)
                .withSimpleCard("HelloWorld", speechText)
                .build();
    }
import com.amazon.ask.dispatcher.request.handler.HandlerInput
import com.amazon.ask.model.Response
import groovy.transform.CompileStatic
import io.micronaut.function.aws.alexa.AlexaIntents
import io.micronaut.function.aws.alexa.annotation.IntentHandler
import javax.inject.Singleton

@Singleton (1)
@CompileStatic
class AlexaApplication {

    private final MessageService messageService

    AlexaApplication(MessageService messageService) { (2)
        this.messageService = messageService
    }

    @IntentHandler("HelloWorldIntent") (3)
    Optional<Response> greet(HandlerInput input) { (4)
        String speechText = messageService.sayHello()
        return input.getResponseBuilder()
                .withSpeech(speechText)
                .withSimpleCard("HelloWorld", speechText)
                .build()
    }
import com.amazon.ask.dispatcher.request.handler.HandlerInput
import com.amazon.ask.model.Response
import io.micronaut.function.aws.alexa.AlexaIntents
import io.micronaut.function.aws.alexa.annotation.IntentHandler

import javax.inject.Singleton
import java.util.Optional

@Singleton (1)
class AlexaApplication(val messageService: MessageService) { (2)

    companion object {
        const val INTENT_NAME = "HelloWorldIntent"
    }

    @IntentHandler(INTENT_NAME) (3)
    fun greet(input : HandlerInput) : Optional<Response> { (4)
        val speechText = messageService.sayHello()
        return input.responseBuilder
                .withSpeech(speechText)
                .withSimpleCard("HelloWorld", speechText)
                .build()
    }
1 The javax.inject.Singleton annotation is used to define AlexApplication as a bean
2 Other services can be dependency injected into the constructor
3 The @IntentHandler is used to indicate which methods are intent handlers
4 The method receives a HandlerInput and returns a Response

7.3 Alexa Skill as a Web Service

The micronaut-aws-webservice is a fork of Amazon Servlet module. It allow you to run your Alexa Skill backend logic in Micronaut applications deployed with a netty or servlet runtimes.

Just by including the dependency:

implementation("io.micronaut.aws:micronaut-aws-alexa-httpserver")
<dependency>
    <groupId>io.micronaut.aws</groupId>
    <artifactId>micronaut-aws-alexa-httpserver</artifactId>
</dependency>

you get an POST endpoint at /alexa (the route is configurable via alexa.endpoint.path.

You can configure your Skill’s endpoint under Build/Endpoint in the Alexa developer console.

7.4 Alexa Skill as an AWS Lambda Function

The micronaut-function-aws-alexa module includes support for building deploying an Alexa Skill as a Lambda Function.

implementation("io.micronaut.aws:micronaut-function-aws-alexa")
<dependency>
    <groupId>io.micronaut.aws</groupId>
    <artifactId>micronaut-function-aws-alexa</artifactId>
</dependency>

As handler specify io.micronaut.function.aws.alexa.AlexaFunction. You don’t need to create a class which extends SkillStreamHandler, AlexaFunction takes care of adding request handlers interceptors etc.

7.5 Flash Briefings

Micronaut’s eases the creation of Flash Briefing Skills.

You can create a flash briefing skill to provide Alexa customers with news headlines and other short content. Typically a flash briefing becomes a part of a customer’s daily routine.

Your application must expose an endpoint which returns a JSON Array of FlashBriefingItem

package io.micronaut.aws.alexa.flashbriefing;

import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;

import javax.validation.Validator;
import java.util.List;
import java.util.stream.Collectors;

@Controller("/news")
public class FlashBriefingsController {

    private final Validator validator;
    private final FlashBriefingRepository flashBriefingRepository;

    public FlashBriefingsController(Validator validator,
                                    FlashBriefingRepository flashBriefingRepository) {
        this.validator = validator;
        this.flashBriefingRepository = flashBriefingRepository;
    }

    @Get (1)
    public List<FlashBriefingItem> index() {
        return flashBriefingRepository.find()
                .stream()
                .filter(item -> validator.validate(item).isEmpty()) (2)
                .sorted() (3)
                .limit(5) (4)
                .collect(Collectors.toList());
    }

}
1 By default, Micronaut sets the response HTTP Header Content-Type with the value application-json.
2 Flash Briefing Feed items must be valid according to Flash Briefing Skill API Feed Reference constraints.
3 Items should be provided in order from newest to oldest, based on the date value for the item. Alexa may ignore older items.
4 Flash Briefing Skill API Feed Reference instructs to provide between 1 and 5 unique items at a time.

8 Repository

You can find the source code of this project in this repository:

9 Breaking Changes

AlexaFunction has been refactored to ease extension.

Table 1. Classes renamed
Old Class Name New Class

io.micronaut.function.aws.alexa.AlexaConfiguration

io.micronaut.aws.alexa.conf.AlexaSkillConfiguration

Table 2. Classes relocated
Class Name Old Package New Package

IntentHandler

io.micronaut.function.aws.alexa.annotation

io.micronaut.aws.alexa.annotation

AnnotatedRequestHandler

io.micronaut.function.aws.alexa.handlers

io.micronaut.aws.alexa.handlers

AlexaIntents

io.micronaut.function.aws.alexa

io.micronaut.aws.alexa.conf

10 Release History

For more information, check the GitHub release list.

1.4

Setting Micronaut 1.2 as the minimum supported version

1.3
  • Improved support for GraalVM native images

  • Upgrade to aws-serverless-java-container-core 1.4

1.2

Compatibility with Micronaut 1.2

1.1

Compatibility with Micronaut 1.1

1.0

Initial Release