io.klerch.alexa.tellask.model.wrapper.AlexaRequestStreamHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of alexa-skills-kit-tellask-java Show documentation
Show all versions of alexa-skills-kit-tellask-java Show documentation
This SDK is an extension to the Alexa Skills SDK for Java. It provides a framework for handling speechlet requests with multi-variant utterances organized in YAML files that make it easy to create localized skills. This SDK also lets you build your skill in declarative style and avoids a lot of boilerplate code.
/**
* Created by Kay Lerch (https://twitter.com/KayLerch)
*
* Contribute to https://github.com/KayLerch/alexa-skills-kit-tellask-java
*
* Attached license applies.
* This source is licensed under GNU GENERAL PUBLIC LICENSE Version 3 as of 29 June 2007
*/
package io.klerch.alexa.tellask.model.wrapper;
import com.amazon.speech.speechlet.SpeechletException;
import com.amazon.speech.speechlet.SpeechletRequestHandler;
import com.amazon.speech.speechlet.SpeechletRequestHandlerException;
import com.amazon.speech.speechlet.verifier.*;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.klerch.alexa.state.handler.AlexaStateHandler;
import io.klerch.alexa.tellask.schema.UtteranceReader;
import io.klerch.alexa.tellask.schema.annotation.AlexaApplication;
import io.klerch.alexa.tellask.util.AlexaRequestHandlerException;
import io.klerch.alexa.tellask.util.factory.AlexaSpeechletFactory;
import io.klerch.alexa.tellask.util.resource.ResourceUtteranceReader;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* An extended version of Lambda's RequestStreamHandler. You must provide some configuration
* to this class by either using the AlexaApplication-annotation or by overriding the getter methods.
* At least you must give it a supported application id otherwise all of the incoming requests will
* be rejected.
*/
public abstract class AlexaRequestStreamHandler implements RequestStreamHandler {
/**
* Provides a set of application-id(s) you can find in the Alexa developer console of your skill.
* Only requests coming in with these application-id(s) pass the request verification.
* @return supported application ids
*/
public Set getSupportedApplicationIds() {
final AlexaApplication app = this.getClass().getAnnotation(AlexaApplication.class);
return app != null ? Stream.of(app.applicationIds()).collect(Collectors.toSet()) : Collections.emptySet();
}
/**
* Override this method to return a customized utterance-reader. This gives
* you freedom of storing your utterance YAML files at any location you desire.
* If you want to decide how to configure an utterance reader individually you
* can set it inside your request handlers by giving it to AlexaOutput. That said
* the value you set right here could be overridden.
* @return customized utterance reader
*/
public UtteranceReader getUtteranceReader() {
return new ResourceUtteranceReader();
}
/**
* Provides the speechlet used to handle the request.
* @return speechlet used to handle the request.
*/
public Class extends AlexaSpeechlet> getSpeechlet() {
final AlexaApplication app = this.getClass().getAnnotation(AlexaApplication.class);
return app != null ? app.speechlet() : AlexaSpeechlet.class;
}
/**
* The handler method is called on a Lambda execution.
* @param input the input stream containing the Lambda request payload
* @param output the output stream containing the Lambda response payload
* @param context a context for a Lambda execution.
* @throws IOException exception is thrown on invalid request payload or on a provided speechlet
* handler having no public constructor taking a String containing the locale
*/
@Override
public void handleRequest(final InputStream input, final OutputStream output, final Context context) throws IOException {
byte[] serializedSpeechletRequest = IOUtils.toByteArray(input);
final AlexaSpeechlet speechlet = AlexaSpeechletFactory.createSpeechletFromRequest(serializedSpeechletRequest, getSpeechlet(), getUtteranceReader());
final SpeechletRequestHandler handler = getRequestStreamHandler();
try {
byte[] outputBytes = handler.handleSpeechletCall(speechlet, serializedSpeechletRequest);
output.write(outputBytes);
} catch (SpeechletRequestHandlerException | SpeechletException e) {
// wrap actual exception in expected IOException
throw new IOException(e);
}
}
/**
* Constructs the stream handler giving it all the provided information.
* @return stream handler
*/
private SpeechletRequestHandler getRequestStreamHandler() {
final Set supportedApplicationIds = getSupportedApplicationIds();
// at least one supported application-id need to be provided
Validate.notEmpty(supportedApplicationIds, "Must provide supported application-id either with overriding the getter or using AlexaApplication-annotation in " + this.getClass().getSimpleName());
return new SpeechletRequestHandler(
Collections.singletonList(
new ApplicationIdSpeechletRequestVerifier(supportedApplicationIds)),
Arrays.asList(
new ResponseSizeSpeechletResponseVerifier(),
new OutputSpeechSpeechletResponseVerifier(),
new CardSpeechletResponseVerifier()));
}
}