From 2f217c5dfe828da55d8e36dbe59a944ba85fa15e Mon Sep 17 00:00:00 2001 From: "Ryan J. Hollander" Date: Sun, 5 Feb 2017 15:30:04 -0500 Subject: [PATCH] fixes the issue: https://github.com/KayLerch/alexa-skills-kit-tellask-java/issues/1 Added "Matcher.quoteReplacement" to resolve of slot input in AlexaSpeechletResponse::resolveSlotsInUtterance. Added test to confirm that slots with replacement text containing $ does not fail with exception Added entry to utterance.yaml for test Minor refactor of resolveSlotsInUtterance, broke out double lambda into a lambda & method to improve code readability (see inline comment) --- .../model/wrapper/AlexaSpeechletResponse.java | 35 ++++++++++--------- .../wrapper/AlexaSpeechletResponseTest.java | 18 ++++++++++ src/test/resources/en-US/utterances.yml | 4 +++ 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/klerch/alexa/tellask/model/wrapper/AlexaSpeechletResponse.java b/src/main/java/io/klerch/alexa/tellask/model/wrapper/AlexaSpeechletResponse.java index b9bf238..a09e712 100644 --- a/src/main/java/io/klerch/alexa/tellask/model/wrapper/AlexaSpeechletResponse.java +++ b/src/main/java/io/klerch/alexa/tellask/model/wrapper/AlexaSpeechletResponse.java @@ -18,15 +18,10 @@ import io.klerch.alexa.tellask.model.AlexaOutputSlot; import io.klerch.alexa.tellask.schema.UtteranceReader; import io.klerch.alexa.tellask.util.resource.YamlReader; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.log4j.Logger; import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -145,11 +140,11 @@ public Reprompt getReprompt() { private String resolveSlotsInUtterance(final String utterance) { final StringBuffer buffer = new StringBuffer(); // extract all the placeholders found in the utterance - final Matcher slots = Pattern.compile("\\{(.*?)\\}").matcher(utterance); + final Matcher slotsInUtterance = Pattern.compile("\\{(.*?)\\}").matcher(utterance); // for any of the placeholders ... - while (slots.find()) { + while (slotsInUtterance.find()) { // ... placeholder-name is the slotName to look after in two places of the output - final String slotName = slots.group(1); + final String slotName = slotsInUtterance.group(1); final AlexaOutputSlot outputSlot = output // prefer directly set output slots .getSlots().stream() @@ -157,18 +152,24 @@ private String resolveSlotsInUtterance(final String utterance) { .filter(slot -> slot.getName().equals(slotName)) .findFirst() // if not directly applied look in provided models for AlexaSlotSave fields - .orElse(output.getModels() - .stream() - // for those having that AlexaSlotSave field - .filter(model -> model.hasOutputSlot(slotName)) - // create a AlexaOutputSlot from attributes in annotation + the field value itself - .map(model -> model.getOutputSlot(slotName).orElse(null)) - .findFirst().orElse(null)); + .orElse(getSavedSlot(slotName)); Validate.notNull(outputSlot, "Could not replace placeholder with name {" + slotName + "} because no corresponding slot was set in the output."); - slots.appendReplacement(buffer, outputSlot.getSsml()); + // RJH - FEB 2017 - Matcher.quoteReplacement on slot input to fix bug + // ~ https://github.com/KayLerch/alexa-skills-kit-tellask-java/issues/1 + slotsInUtterance.appendReplacement(buffer, Matcher.quoteReplacement(outputSlot.getSsml())); } - slots.appendTail(buffer); + slotsInUtterance.appendTail(buffer); return "" + buffer.toString() + ""; } + + private AlexaOutputSlot getSavedSlot(String slotName) { + return output.getModels() + .stream() + // for those having that AlexaSlotSave field + .filter(model -> model.hasOutputSlot(slotName)) + // create a AlexaOutputSlot from attributes in annotation + the field value itself + .map(model -> model.getOutputSlot(slotName).orElse(null)) + .findFirst().orElse(null); + } } diff --git a/src/test/java/io/klerch/alexa/tellask/model/wrapper/AlexaSpeechletResponseTest.java b/src/test/java/io/klerch/alexa/tellask/model/wrapper/AlexaSpeechletResponseTest.java index f8942af..db6835d 100644 --- a/src/test/java/io/klerch/alexa/tellask/model/wrapper/AlexaSpeechletResponseTest.java +++ b/src/test/java/io/klerch/alexa/tellask/model/wrapper/AlexaSpeechletResponseTest.java @@ -64,6 +64,24 @@ public void getResponseWithSlots() throws Exception { ((SsmlOutputSpeech)response.getOutputSpeech()).getSsml()); Assert.assertNull(response.getReprompt()); } + // RJH FEB 2017 - added test for issue: + // ~ https://github.com/KayLerch/alexa-skills-kit-tellask-java/issues/1 + @Test + public void getResponseWithSlotsMatcherQuoteReplacement() throws Exception { + final AlexaStateModelSample model = new AlexaStateModelSample(); + model.setName("Paul"); + + final AlexaOutput output = AlexaOutput + .ask("IntentWithOneSlotReplacement") + .putSlot("theSlot", "$5.00") + .putState(model).build(); + + final AlexaSpeechletResponse response = new AlexaSpeechletResponse(output, new ResourceUtteranceReader(), "en-US"); + Assert.assertEquals(output, response.getOutput()); + Assert.assertEquals("This is a single slot replacement: $5.00", + ((SsmlOutputSpeech)response.getOutputSpeech()).getSsml()); + Assert.assertNull(response.getReprompt()); + } @Test public void getResponseWithReprompt() throws Exception { diff --git a/src/test/resources/en-US/utterances.yml b/src/test/resources/en-US/utterances.yml index 1c9543f..54b92e0 100644 --- a/src/test/resources/en-US/utterances.yml +++ b/src/test/resources/en-US/utterances.yml @@ -43,6 +43,10 @@ IntentWithOneUtteranceAndOneReprompt: Reprompts: - "This is a reprompt {name} with your score of {credits}" +IntentWithOneSlotReplacement: + Utterances: + - "This is a single slot replacement: {theSlot}" + IntentWithInstantUtterance: - "Hello there {name}. Your current score is {credits} This is awesome" - "Welcome {name}. You got a score of {credits} You are awesome"