Skip to content

Commit

Permalink
Add support for templated responses (#195)
Browse files Browse the repository at this point in the history
  • Loading branch information
breedloj authored Apr 18, 2019
1 parent 6097fd1 commit fa8f81f
Show file tree
Hide file tree
Showing 35 changed files with 2,218 additions and 22 deletions.
8 changes: 8 additions & 0 deletions ask-sdk-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@
<build>
<sourceDirectory>src</sourceDirectory>
<testSourceDirectory>tst</testSourceDirectory>
<testResources>
<testResource>
<directory>tst</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
</testResources>
<pluginManagement>
<plugins>
<plugin>
Expand Down
16 changes: 13 additions & 3 deletions ask-sdk-core/src/com/amazon/ask/CustomSkill.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,24 @@
import com.amazon.ask.builder.CustomSkillConfiguration;
import com.amazon.ask.dispatcher.request.handler.HandlerInput;
import com.amazon.ask.exception.AskSdkException;
import com.amazon.ask.impl.AbstractSkill;
import com.amazon.ask.model.RequestEnvelope;
import com.amazon.ask.model.Response;
import com.amazon.ask.model.ResponseEnvelope;
import com.amazon.ask.model.services.ApiClient;
import com.amazon.ask.model.services.ApiConfiguration;
import com.amazon.ask.model.services.DefaultApiConfiguration;
import com.amazon.ask.model.services.Serializer;
import com.amazon.ask.model.services.ServiceClientFactory;
import com.amazon.ask.request.dispatcher.GenericRequestDispatcher;
import com.amazon.ask.request.dispatcher.impl.BaseRequestDispatcher;
import com.amazon.ask.response.template.TemplateFactory;
import com.amazon.ask.util.JacksonSerializer;
import com.amazon.ask.util.SdkConstants;
import com.amazon.ask.util.UserAgentUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.amazon.ask.util.impl.JacksonJsonMarshaller;
import com.amazon.ask.util.impl.JacksonJsonUnmarshaller;

import java.util.Optional;

Expand All @@ -34,6 +42,7 @@ public class CustomSkill extends AbstractSkill<RequestEnvelope, ResponseEnvelope
protected final Serializer serializer;
protected final String customUserAgent;
protected final String skillId;
protected final TemplateFactory<HandlerInput, Response> templateFactory;

public CustomSkill(CustomSkillConfiguration configuration) {
super(JacksonJsonUnmarshaller.withTypeBinding(RequestEnvelope.class, "request"),
Expand All @@ -50,6 +59,7 @@ public CustomSkill(CustomSkillConfiguration configuration) {
this.serializer = new JacksonSerializer();
this.customUserAgent = configuration.getCustomUserAgent();
this.skillId = configuration.getSkillId();
this.templateFactory = configuration.getTemplateFactory();
}

public ResponseEnvelope invoke(RequestEnvelope requestEnvelope) {
Expand All @@ -75,16 +85,17 @@ protected ResponseEnvelope invoke(UnmarshalledRequest<RequestEnvelope> unmarshal
throw new AskSdkException("AlexaSkill ID verification failed.");
}

ServiceClientFactory factory = apiClient != null ? ServiceClientFactory.builder()
ServiceClientFactory serviceClientFactory = apiClient != null ? ServiceClientFactory.builder()
.withDefaultApiConfiguration(getApiConfiguration(requestEnvelope))
.build() : null;

HandlerInput handlerInput = HandlerInput.builder()
.withRequestEnvelope(requestEnvelope)
.withPersistenceAdapter(persistenceAdapter)
.withContext(context)
.withServiceClientFactory(factory)
.withRequestEnvelopeJson(requestEnvelopeJson)
.withServiceClientFactory(serviceClientFactory)
.withTemplateFactory(templateFactory)
.build();

Optional<Response> response = requestDispatcher.dispatch(handlerInput);
Expand All @@ -108,5 +119,4 @@ protected ApiConfiguration getApiConfiguration(RequestEnvelope requestEnvelope)
.build();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.amazon.ask.dispatcher.request.handler.HandlerInput;
import com.amazon.ask.model.Response;
import com.amazon.ask.model.services.ApiClient;
import com.amazon.ask.response.template.TemplateFactory;

import java.util.Optional;

Expand All @@ -30,4 +31,6 @@ public interface CustomSkillConfiguration extends GenericSkillConfiguration<Hand

ApiClient getApiClient();

TemplateFactory<HandlerInput, Response> getTemplateFactory();

}
12 changes: 10 additions & 2 deletions ask-sdk-core/src/com/amazon/ask/builder/SkillBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@
package com.amazon.ask.builder;

import com.amazon.ask.Skill;
import com.amazon.ask.builder.impl.AbstractSkillBuilder;
import com.amazon.ask.attributes.persistence.PersistenceAdapter;
import com.amazon.ask.builder.impl.AbstractSkillBuilder;
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.model.services.ApiClient;
import com.amazon.ask.module.SdkModule;
import com.amazon.ask.module.SdkModuleContext;
import com.amazon.ask.request.handler.adapter.impl.BaseHandlerAdapter;
import com.amazon.ask.response.template.TemplateFactory;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -34,6 +35,7 @@ public class SkillBuilder<T extends SkillBuilder<T>> extends AbstractSkillBuilde
protected PersistenceAdapter persistenceAdapter;
protected ApiClient apiClient;
protected String skillId;
protected TemplateFactory<HandlerInput, Response> templateFactory;

public SkillBuilder() {
this.sdkModules = new ArrayList<>();
Expand All @@ -59,6 +61,11 @@ public T withSkillId(String skillId) {
return getThis();
}

public T withTemplateFactory(TemplateFactory templateFactory) {
this.templateFactory = templateFactory;
return getThis();
}

@SuppressWarnings("unchecked")
private T getThis() {
return (T) this;
Expand All @@ -75,7 +82,8 @@ protected SkillConfiguration.Builder getConfigBuilder() {

skillConfigBuilder.withPersistenceAdapter(persistenceAdapter)
.withApiClient(apiClient)
.withSkillId(skillId);
.withSkillId(skillId)
.withTemplateFactory(templateFactory);

SdkModuleContext sdkModuleContext = new SdkModuleContext(skillConfigBuilder);
for (SdkModule sdkModule : sdkModules) {
Expand Down
46 changes: 37 additions & 9 deletions ask-sdk-core/src/com/amazon/ask/builder/SkillConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@
import com.amazon.ask.request.interceptor.GenericRequestInterceptor;
import com.amazon.ask.request.interceptor.GenericResponseInterceptor;
import com.amazon.ask.request.mapper.GenericRequestMapper;
import com.amazon.ask.response.template.TemplateFactory;
import com.amazon.ask.response.template.loader.TemplateLoader;
import com.amazon.ask.response.template.renderer.TemplateRenderer;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

Expand All @@ -36,28 +40,36 @@ public class SkillConfiguration extends AbstractSkillConfiguration<HandlerInput,
protected final String skillId;
protected final PersistenceAdapter persistenceAdapter;
protected final ApiClient apiClient;
protected final TemplateFactory<HandlerInput, Response> templateFactory;

protected SkillConfiguration(List<GenericRequestMapper<HandlerInput, Optional<Response>>> requestMappers,
List<GenericHandlerAdapter<HandlerInput, Optional<Response>>> handlerAdapters,
GenericExceptionMapper<HandlerInput, Optional<Response>> exceptionMapper,
PersistenceAdapter persistenceAdapter, ApiClient apiClient,
String customUserAgent, String skillId) {
PersistenceAdapter persistenceAdapter,
ApiClient apiClient,
String customUserAgent,
String skillId,
TemplateFactory<HandlerInput, Response> templateFactory) {
this(requestMappers, handlerAdapters, null, null, exceptionMapper,
persistenceAdapter, apiClient, customUserAgent, skillId);
persistenceAdapter, apiClient, customUserAgent, skillId, templateFactory);
}

protected SkillConfiguration(List<GenericRequestMapper<HandlerInput, Optional<Response>>> requestMappers,
List<GenericHandlerAdapter<HandlerInput, Optional<Response>>> handlerAdapters,
List<GenericRequestInterceptor<HandlerInput>> requestInterceptors,
List<GenericResponseInterceptor<HandlerInput, Optional<Response>>> responseInterceptors,
GenericExceptionMapper<HandlerInput, Optional<Response>> exceptionMapper,
PersistenceAdapter persistenceAdapter, ApiClient apiClient,
String customUserAgent, String skillId) {
PersistenceAdapter persistenceAdapter,
ApiClient apiClient,
String customUserAgent,
String skillId,
TemplateFactory<HandlerInput, Response> templateFactory) {
super(requestMappers, handlerAdapters, requestInterceptors, responseInterceptors, exceptionMapper);
this.customUserAgent = customUserAgent;
this.skillId = skillId;
this.persistenceAdapter = persistenceAdapter;
this.apiClient = apiClient;
this.templateFactory = templateFactory;
}

public static Builder builder() {
Expand All @@ -80,11 +92,19 @@ public ApiClient getApiClient() {
return apiClient;
}

public TemplateFactory<HandlerInput, Response> getTemplateFactory() {
return templateFactory;
}

public static final class Builder extends AbstractSkillConfiguration.Builder<HandlerInput, Optional<Response>, Builder> {
private PersistenceAdapter persistenceAdapter;
private ApiClient apiClient;
private String customUserAgent;
private String skillId;
private TemplateFactory<HandlerInput, Response> templateFactory;

public Builder() {
}

public Builder withPersistenceAdapter(PersistenceAdapter persistenceAdapter) {
this.persistenceAdapter = persistenceAdapter;
Expand Down Expand Up @@ -127,11 +147,19 @@ public String getSkillId() {
return skillId;
}

public SkillConfiguration build() {
return new SkillConfiguration(requestMappers, handlerAdapters, requestInterceptors, responseInterceptors,
exceptionMapper, persistenceAdapter, apiClient, customUserAgent, skillId);
public Builder withTemplateFactory(TemplateFactory<HandlerInput, Response> templateFactory) {
this.templateFactory = templateFactory;
return this;
}

public TemplateFactory<HandlerInput, Response> getTemplateFactory() {
return templateFactory;
}

public SkillConfiguration build() {
return new SkillConfiguration(requestMappers, handlerAdapters, requestInterceptors, responseInterceptors,
exceptionMapper, persistenceAdapter, apiClient, customUserAgent, skillId, templateFactory);
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

package com.amazon.ask.dispatcher.request.handler;

import com.amazon.ask.exception.template.TemplateFactoryException;
import com.amazon.ask.model.Response;
import com.amazon.ask.request.exception.handler.impl.AbstractHandlerInput;
import com.amazon.ask.attributes.AttributesManager;
import com.amazon.ask.attributes.persistence.PersistenceAdapter;
Expand All @@ -22,7 +24,10 @@
import com.amazon.ask.response.ResponseBuilder;
import com.amazon.ask.util.ValidationUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.amazon.ask.response.template.TemplateFactory;

import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;

/**
Expand All @@ -40,9 +45,11 @@ public class HandlerInput extends AbstractHandlerInput<Request> {
protected final ServiceClientFactory serviceClientFactory;
protected final ResponseBuilder responseBuilder;
protected final JsonNode requestEnvelopeJson;
protected final TemplateFactory<HandlerInput, Response> templateFactory;

protected HandlerInput(RequestEnvelope requestEnvelope, PersistenceAdapter persistenceAdapter,
Object context, ServiceClientFactory serviceClientFactory, JsonNode requestEnvelopeJson) {
Object context, ServiceClientFactory serviceClientFactory,
JsonNode requestEnvelopeJson, TemplateFactory<HandlerInput, Response> templateFactory) {
super(ValidationUtils.assertNotNull(requestEnvelope, "request envelope").getRequest(), context);
this.requestEnvelope = requestEnvelope;
this.serviceClientFactory = serviceClientFactory;
Expand All @@ -52,12 +59,29 @@ protected HandlerInput(RequestEnvelope requestEnvelope, PersistenceAdapter persi
.build();
this.responseBuilder = new ResponseBuilder();
this.requestEnvelopeJson = requestEnvelopeJson;
this.templateFactory = templateFactory;
}

public static Builder builder() {
return new Builder();
}

/**
* Generate {@link Response} using skill response template and injecting data.
* Response template contains response components including but not limited to
* {@link com.amazon.ask.model.ui.OutputSpeech}, {@link com.amazon.ask.model.ui.Card}, {@link com.amazon.ask.model.Directive} and {@link com.amazon.ask.model.canfulfill.CanFulfillIntent}
* and placeholders for injecting data.
* Injecting data provides component values to be injected into template.
*
* @param responseTemplateName name of response template
* @param dataMap a map that contains injecting data
* @return skill response
* @throws TemplateFactoryException if fail to load or render template using provided {@link com.amazon.ask.response.template.loader.TemplateLoader} or {@link com.amazon.ask.response.template.renderer.TemplateRenderer}
*/
public Optional<Response> generateTemplateResponse(String responseTemplateName, Map<String, Object> dataMap) throws TemplateFactoryException {
return Optional.of(templateFactory.processTemplate(responseTemplateName, dataMap, this));
}

/**
* Returns the {@link RequestEnvelope} of the incoming request.
*
Expand Down Expand Up @@ -120,6 +144,7 @@ public static final class Builder extends AbstractHandlerInput.Builder<Request,
private PersistenceAdapter persistenceAdapter;
private ServiceClientFactory serviceClientFactory;
private JsonNode requestEnvelopeJson;
private TemplateFactory templateFactory;

private Builder() {
}
Expand All @@ -144,8 +169,13 @@ public Builder withRequestEnvelopeJson(JsonNode requestEnvelopeJson) {
return this;
}

public Builder withTemplateFactory(TemplateFactory templateFactory) {
this.templateFactory = templateFactory;
return this;
}

public HandlerInput build() {
return new HandlerInput(requestEnvelope, persistenceAdapter, context, serviceClientFactory, requestEnvelopeJson);
return new HandlerInput(requestEnvelope, persistenceAdapter, context, serviceClientFactory, requestEnvelopeJson, templateFactory);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file
except in compliance with the License. A copy of the License is located at
http://aws.amazon.com/apache2.0/
or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for
the specific language governing permissions and limitations under the License.
*/

package com.amazon.ask.response.template.loader.impl;

import com.amazon.ask.dispatcher.request.handler.HandlerInput;
import com.amazon.ask.response.template.loader.TemplateCache;
import com.amazon.ask.response.template.loader.TemplateEnumerator;

import java.util.function.BiFunction;

/**
* {@inheritDoc}
*
* Use {@link LocaleTemplateEnumerator} as a default {@link TemplateEnumerator} in LocalTemplateFileLoader if no TemplateEnumerator provided.
*/
public class LocalTemplateFileLoader extends AbstractLocalTemplateFileLoader<HandlerInput> {

protected LocalTemplateFileLoader(String directoryPath, String fileExtension,
ClassLoader classLoader, TemplateCache templateCache,
BiFunction<String, HandlerInput, TemplateEnumerator<HandlerInput>> templateEnumeratorSupplier) {
super(directoryPath, fileExtension, classLoader, templateCache, templateEnumeratorSupplier);
}

public static Builder builder() {
return new Builder();
}

public static final class Builder extends AbstractLocalTemplateFileLoader.Builder<HandlerInput, Builder> {

public LocalTemplateFileLoader build() {
return new LocalTemplateFileLoader(directoryPath, fileExtension,
classLoader, templateCache,
templateEnumeratorSupplier == null
? (BiFunction<String, HandlerInput, TemplateEnumerator<HandlerInput>>) (s, handlerInput) -> LocaleTemplateEnumerator.builder().withTemplateName(s).withHandlerInput(handlerInput).build()
: templateEnumeratorSupplier);
}
}

}
Loading

0 comments on commit fa8f81f

Please sign in to comment.