From 94725eaee50e783b4f05dbbbb8318abc437e1edd Mon Sep 17 00:00:00 2001 From: Sergey Greenko Date: Tue, 5 Dec 2017 08:44:31 +0200 Subject: [PATCH 01/16] Add common SpeechletRequestHandler --- AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj | 2 +- AlexaSkillsKit.Lib/HttpHelpers.cs | 33 -- .../Speechlet/ISpeechletAsync.cs | 2 +- AlexaSkillsKit.Lib/Speechlet/Speechlet.cs | 8 +- .../Speechlet/SpeechletAsync.cs | 14 +- .../Speechlet/SpeechletRequestHandler.cs | 289 ++++++++++++++++++ 6 files changed, 302 insertions(+), 46 deletions(-) delete mode 100644 AlexaSkillsKit.Lib/HttpHelpers.cs create mode 100644 AlexaSkillsKit.Lib/Speechlet/SpeechletRequestHandler.cs diff --git a/AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj b/AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj index 3ddf3c1..2ebfa16 100644 --- a/AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj +++ b/AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj @@ -81,7 +81,6 @@ - @@ -91,6 +90,7 @@ + diff --git a/AlexaSkillsKit.Lib/HttpHelpers.cs b/AlexaSkillsKit.Lib/HttpHelpers.cs deleted file mode 100644 index e0a9845..0000000 --- a/AlexaSkillsKit.Lib/HttpHelpers.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. - -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Net.Http.Formatting; -using System.Text; -using System.Threading.Tasks; - -namespace AlexaSkillsKit -{ - public static class HttpHelpers - { - /// - /// - /// - public static string ToLogString(this HttpRequestMessage httpRequest) { - var serializedRequest = AsyncHelpers.RunSync(() => - new HttpMessageContent(httpRequest).ReadAsByteArrayAsync()); - return UTF8Encoding.UTF8.GetString(serializedRequest); - } - - - /// - /// - /// - public static string ToLogString(this HttpResponseMessage httpResponse) { - var serializedRequest = AsyncHelpers.RunSync(() => - new HttpMessageContent(httpResponse).ReadAsByteArrayAsync()); - return UTF8Encoding.UTF8.GetString(serializedRequest); - } - } -} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs b/AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs index 240e919..93cbd97 100644 --- a/AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs +++ b/AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs @@ -9,7 +9,7 @@ namespace AlexaSkillsKit.Speechlet { public interface ISpeechletAsync { - bool OnRequestValidation( + Task OnRequestValidationAsync( SpeechletRequestValidationResult result, DateTime referenceTimeUtc, SpeechletRequestEnvelope requestEnvelope); Task OnAudioPlayerAsync(AudioPlayerRequest audioRequest, Context context); diff --git a/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs b/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs index 3eb5064..74e368d 100644 --- a/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs +++ b/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs @@ -35,8 +35,9 @@ public virtual HttpResponseMessage GetResponse(HttpRequestMessage httpRequest) { validationResult = validationResult | SpeechletRequestValidationResult.NoSignatureHeader; } - var alexaBytes = AsyncHelpers.RunSync(() => httpRequest.Content.ReadAsByteArrayAsync()); - Debug.WriteLine(httpRequest.ToLogString()); + var alexaBytes = AsyncHelpers.RunSync(() => httpRequest.Content.ReadAsByteArrayAsync()); + var alexaContent = Encoding.UTF8.GetString(alexaBytes); + Debug.WriteLine(alexaContent); // attempt to verify signature only if we were able to locate certificate and signature headers if (validationResult == SpeechletRequestValidationResult.OK) { @@ -47,7 +48,6 @@ public virtual HttpResponseMessage GetResponse(HttpRequestMessage httpRequest) { SpeechletRequestEnvelope alexaRequest = null; try { - var alexaContent = UTF8Encoding.UTF8.GetString(alexaBytes); alexaRequest = SpeechletRequestEnvelope.FromJson(alexaContent); } catch (Exception ex) @@ -69,6 +69,7 @@ public virtual HttpResponseMessage GetResponse(HttpRequestMessage httpRequest) { } string alexaResponse = DoProcessRequest(alexaRequest); + Debug.WriteLine(alexaResponse); HttpResponseMessage httpResponse; if (alexaResponse == null) { @@ -77,7 +78,6 @@ public virtual HttpResponseMessage GetResponse(HttpRequestMessage httpRequest) { else { httpResponse = new HttpResponseMessage(HttpStatusCode.OK); httpResponse.Content = new StringContent(alexaResponse, Encoding.UTF8, "application/json"); - Debug.WriteLine(httpResponse.ToLogString()); } return httpResponse; diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs index 98fff63..d74cd02 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs @@ -37,7 +37,8 @@ public async virtual Task GetResponseAsync(HttpRequestMessa } var alexaBytes = await httpRequest.Content.ReadAsByteArrayAsync(); - Debug.WriteLine(httpRequest.ToLogString()); + var alexaContent = Encoding.UTF8.GetString(alexaBytes); + Debug.WriteLine(alexaContent); // attempt to verify signature only if we were able to locate certificate and signature headers if (validationResult == SpeechletRequestValidationResult.OK) { @@ -48,7 +49,6 @@ public async virtual Task GetResponseAsync(HttpRequestMessa SpeechletRequestEnvelope alexaRequest = null; try { - var alexaContent = UTF8Encoding.UTF8.GetString(alexaBytes); alexaRequest = SpeechletRequestEnvelope.FromJson(alexaContent); } catch (Exception ex) @@ -63,14 +63,15 @@ public async virtual Task GetResponseAsync(HttpRequestMessa } } - if (alexaRequest == null || !OnRequestValidation(validationResult, now, alexaRequest)) { + if (alexaRequest == null || !await OnRequestValidationAsync(validationResult, now, alexaRequest)) { return new HttpResponseMessage(HttpStatusCode.BadRequest) { ReasonPhrase = validationResult.ToString() }; } string alexaResponse = await DoProcessRequestAsync(alexaRequest); - + Debug.WriteLine(alexaResponse); + HttpResponseMessage httpResponse; if (alexaResponse == null) { httpResponse = new HttpResponseMessage(HttpStatusCode.InternalServerError); @@ -78,7 +79,6 @@ public async virtual Task GetResponseAsync(HttpRequestMessa else { httpResponse = new HttpResponseMessage(HttpStatusCode.OK); httpResponse.Content = new StringContent(alexaResponse, Encoding.UTF8, "application/json"); - Debug.WriteLine(httpResponse.ToLogString()); } return httpResponse; @@ -197,10 +197,10 @@ private void DoSessionManagement(IntentRequest request, Session session) { /// Opportunity to set policy for handling requests with invalid signatures and/or timestamps /// /// true if request processing should continue, otherwise false - public virtual bool OnRequestValidation( + public virtual Task OnRequestValidationAsync( SpeechletRequestValidationResult result, DateTime referenceTimeUtc, SpeechletRequestEnvelope requestEnvelope) { - return result == SpeechletRequestValidationResult.OK; + return Task.FromResult(result == SpeechletRequestValidationResult.OK); } diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletRequestHandler.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletRequestHandler.cs new file mode 100644 index 0000000..0860fd8 --- /dev/null +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletRequestHandler.cs @@ -0,0 +1,289 @@ +// Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. + +using System; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; +using AlexaSkillsKit.Json; +using AlexaSkillsKit.Authentication; + +namespace AlexaSkillsKit.Speechlet +{ + public class SpeechletRequestHandler + { + private readonly ISpeechlet speechlet; + private readonly ISpeechletAsync speechletAsync; + + public SpeechletRequestHandler(ISpeechlet speechlet) { + this.speechlet = speechlet; + } + + public SpeechletRequestHandler(ISpeechletAsync speechletAsync) { + this.speechletAsync = speechletAsync; + } + + public HttpResponseMessage GetResponse(HttpRequestMessage httpRequest) { + return AsyncHelpers.RunSync(() => GetResponseAsync(httpRequest)); + } + + /// + /// Processes Alexa request but does NOT validate request signature + /// + /// + /// + public string ProcessRequest(string requestContent) { + var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); + return AsyncHelpers.RunSync(() => DoProcessRequestAsync(requestEnvelope)); + } + + + /// + /// Processes Alexa request but does NOT validate request signature + /// + /// + /// + public string ProcessRequest(JObject requestJson) { + var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); + return AsyncHelpers.RunSync(() => DoProcessRequestAsync(requestEnvelope)); + } + + /// + /// Processes Alexa request AND validates request signature + /// + /// + /// + public async virtual Task GetResponseAsync(HttpRequestMessage httpRequest) { + SpeechletRequestValidationResult validationResult = SpeechletRequestValidationResult.OK; + DateTime now = DateTime.UtcNow; // reference time for this request + + string chainUrl = null; + if (!httpRequest.Headers.Contains(Sdk.SIGNATURE_CERT_URL_REQUEST_HEADER) || + String.IsNullOrEmpty(chainUrl = httpRequest.Headers.GetValues(Sdk.SIGNATURE_CERT_URL_REQUEST_HEADER).First())) { + validationResult = validationResult | SpeechletRequestValidationResult.NoCertHeader; + } + + string signature = null; + if (!httpRequest.Headers.Contains(Sdk.SIGNATURE_REQUEST_HEADER) || + String.IsNullOrEmpty(signature = httpRequest.Headers.GetValues(Sdk.SIGNATURE_REQUEST_HEADER).First())) { + validationResult = validationResult | SpeechletRequestValidationResult.NoSignatureHeader; + } + + var alexaBytes = await httpRequest.Content.ReadAsByteArrayAsync(); + var alexaContent = Encoding.UTF8.GetString(alexaBytes); + Debug.WriteLine(alexaContent); + + // attempt to verify signature only if we were able to locate certificate and signature headers + if (validationResult == SpeechletRequestValidationResult.OK) { + if (!(await SpeechletRequestSignatureVerifier.VerifyRequestSignatureAsync(alexaBytes, signature, chainUrl))) { + validationResult = validationResult | SpeechletRequestValidationResult.InvalidSignature; + } + } + + SpeechletRequestEnvelope alexaRequest = null; + try { + alexaRequest = SpeechletRequestEnvelope.FromJson(alexaContent); + } + catch (Exception ex) + when (ex is Newtonsoft.Json.JsonReaderException || ex is InvalidCastException || ex is FormatException) { + validationResult = validationResult | SpeechletRequestValidationResult.InvalidJson; + } + + // attempt to verify timestamp only if we were able to parse request body + if (alexaRequest != null) { + if (!SpeechletRequestTimestampVerifier.VerifyRequestTimestamp(alexaRequest, now)) { + validationResult = validationResult | SpeechletRequestValidationResult.InvalidTimestamp; + } + } + + if (alexaRequest == null || !await OnRequestValidationAsync(validationResult, now, alexaRequest)) { + return new HttpResponseMessage(HttpStatusCode.BadRequest) { + ReasonPhrase = validationResult.ToString() + }; + } + + string alexaResponse = await DoProcessRequestAsync(alexaRequest); + Debug.WriteLine(alexaResponse); + + HttpResponseMessage httpResponse; + if (alexaResponse == null) { + httpResponse = new HttpResponseMessage(HttpStatusCode.InternalServerError); + } + else { + httpResponse = new HttpResponseMessage(HttpStatusCode.OK) { + Content = new StringContent(alexaResponse, Encoding.UTF8, "application/json") + }; + } + + return httpResponse; + } + + + /// + /// Processes Alexa request but does NOT validate request signature + /// + /// + /// + public async virtual Task ProcessRequestAsync(string requestContent) { + var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); + return await DoProcessRequestAsync(requestEnvelope); + } + + + /// + /// Processes Alexa request but does NOT validate request signature + /// + /// + /// + public async virtual Task ProcessRequestAsync(JObject requestJson) { + var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); + return await DoProcessRequestAsync(requestEnvelope); + } + + + /// + /// + /// + /// + /// + private async Task DoProcessRequestAsync(SpeechletRequestEnvelope requestEnvelope) { + Session session = requestEnvelope.Session; + var context = requestEnvelope.Context; + var request = requestEnvelope.Request; + ISpeechletResponse response = null; + + // Do session management prior to calling OnSessionStarted and OnIntentAsync + // to allow dev to change session values if behavior is not desired + DoSessionManagement(request as IntentRequest, session); + + if (requestEnvelope.Session.IsNew) { + await OnSessionStartedAsync( + new SessionStartedRequest(request.RequestId, request.Timestamp, request.Locale), session); + } + + // process launch request + if (requestEnvelope.Request is LaunchRequest) { + response = await OnLaunchAsync(request as LaunchRequest, session); + } + + // process audio player request + else if (requestEnvelope.Request is AudioPlayerRequest) { + response = await OnAudioPlayerAsync(request as AudioPlayerRequest, context); + } + + // process playback controller request + else if (requestEnvelope.Request is PlaybackControllerRequest) { + response = await OnPlaybackControllerAsync(request as PlaybackControllerRequest, context); + } + + // process system request + else if (requestEnvelope.Request is SystemExceptionEncounteredRequest) { + await OnSystemExceptionEncounteredAsync(request as SystemExceptionEncounteredRequest, context); + } + + // process intent request + else if (requestEnvelope.Request is IntentRequest) { + response = await OnIntentAsync(request as IntentRequest, session, context); + } + + // process session ended request + else if (requestEnvelope.Request is SessionEndedRequest) { + await OnSessionEndedAsync(request as SessionEndedRequest, session); + } + + var responseEnvelope = new SpeechletResponseEnvelope { + Version = requestEnvelope.Version, + Response = response, + SessionAttributes = session.Attributes + }; + return responseEnvelope.ToJson(); + } + + + /// + /// + /// + private void DoSessionManagement(IntentRequest request, Session session) { + if (request == null) return; + + if (session.IsNew) { + session.Attributes[Session.INTENT_SEQUENCE] = request.Intent.Name; + } + else { + // if the session was started as a result of a launch request + // a first intent isn't yet set, so set it to the current intent + if (!session.Attributes.ContainsKey(Session.INTENT_SEQUENCE)) { + session.Attributes[Session.INTENT_SEQUENCE] = request.Intent.Name; + } + else { + session.Attributes[Session.INTENT_SEQUENCE] += Session.SEPARATOR + request.Intent.Name; + } + } + + // Auto-session management: copy all slot values from current intent into session + foreach (var slot in request.Intent.Slots.Values) { + if (!String.IsNullOrEmpty(slot.Value)) session.Attributes[slot.Name] = slot.Value; + } + } + + private async Task OnAudioPlayerAsync(AudioPlayerRequest audioRequest, Context context) { + return speechlet != null ? + speechlet.OnAudioPlayer(audioRequest, context) + : + await speechletAsync.OnAudioPlayerAsync(audioRequest, context); + } + + private async Task OnPlaybackControllerAsync(PlaybackControllerRequest playbackRequest, Context context) { + return speechlet is ISpeechlet ? + (speechlet as ISpeechlet).OnPlaybackController(playbackRequest, context) + : + await (speechlet as ISpeechletAsync).OnPlaybackControllerAsync(playbackRequest, context); + } + + private async Task OnSystemExceptionEncounteredAsync(SystemExceptionEncounteredRequest systemRequest, Context context) { + if (speechlet is ISpeechlet) + (speechlet as ISpeechlet).OnSystemExceptionEncountered(systemRequest, context); + else + await (speechlet as ISpeechletAsync).OnSystemExceptionEncounteredAsync(systemRequest, context); + } + + private async Task OnIntentAsync(IntentRequest intentRequest, Session session, Context context) { + return speechlet is ISpeechlet ? + (speechlet as ISpeechlet).OnIntent(intentRequest, session, context) + : + await (speechlet as ISpeechletAsync).OnIntentAsync(intentRequest, session, context); + } + + private async Task OnLaunchAsync(LaunchRequest launchRequest, Session session) { + return speechlet is ISpeechlet ? + (speechlet as ISpeechlet).OnLaunch(launchRequest, session) + : + await (speechlet as ISpeechletAsync).OnLaunchAsync(launchRequest, session); + } + + + private async Task OnSessionEndedAsync(SessionEndedRequest sessionEndedRequest, Session session) { + if (speechlet is ISpeechlet) + (speechlet as ISpeechlet).OnSessionEnded(sessionEndedRequest, session); + else + await (speechlet as ISpeechletAsync).OnSessionEndedAsync(sessionEndedRequest, session); + } + + private async Task OnSessionStartedAsync(SessionStartedRequest sessionStartedRequest, Session session) { + if (speechlet is ISpeechlet) + (speechlet as ISpeechlet).OnSessionStarted(sessionStartedRequest, session); + else + await (speechlet as ISpeechletAsync).OnSessionStartedAsync(sessionStartedRequest, session); + } + + private async Task OnRequestValidationAsync(SpeechletRequestValidationResult result, DateTime referenceTimeUtc, SpeechletRequestEnvelope requestEnvelope) { + return speechlet is ISpeechlet ? + (speechlet as ISpeechlet).OnRequestValidation(result, referenceTimeUtc, requestEnvelope) + : + await (speechlet as ISpeechletAsync).OnRequestValidationAsync(result, referenceTimeUtc, requestEnvelope); + } + } +} \ No newline at end of file From 652a1c514dde151810f844fd2fa666d9afb05f1f Mon Sep 17 00:00:00 2001 From: Sergey Greenko Date: Thu, 7 Dec 2017 01:14:22 +0200 Subject: [PATCH 02/16] Removed speechlet code duplication, Speechlet and SpeechletAsync are marked as obsolete (In favor of direct interface implementation) --- AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj | 2 + AlexaSkillsKit.Lib/Speechlet/Speechlet.cs | 159 +---------------- .../Speechlet/SpeechletAsync.cs | 165 +----------------- .../Speechlet/SpeechletAsyncWrapper.cs | 51 ++++++ .../Speechlet/SpeechletExtensions.cs | 99 +++++++++++ .../Speechlet/SpeechletRequestHandler.cs | 153 +++------------- 6 files changed, 186 insertions(+), 443 deletions(-) create mode 100644 AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs create mode 100644 AlexaSkillsKit.Lib/Speechlet/SpeechletExtensions.cs diff --git a/AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj b/AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj index 2ebfa16..5863eee 100644 --- a/AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj +++ b/AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj @@ -90,6 +90,8 @@ + + diff --git a/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs b/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs index 74e368d..eb176e3 100644 --- a/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs +++ b/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs @@ -1,17 +1,14 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. using System; -using System.Diagnostics; -using System.Linq; -using System.Net; using System.Net.Http; -using System.Text; using Newtonsoft.Json.Linq; using AlexaSkillsKit.Authentication; using AlexaSkillsKit.Json; namespace AlexaSkillsKit.Speechlet { + [Obsolete("Speechlet base class is obselete and will be removed in a future versions of this library. Implement ISpeechlet interface directly instead.")] public abstract class Speechlet : ISpeechlet { /// @@ -20,67 +17,7 @@ public abstract class Speechlet : ISpeechlet /// /// public virtual HttpResponseMessage GetResponse(HttpRequestMessage httpRequest) { - SpeechletRequestValidationResult validationResult = SpeechletRequestValidationResult.OK; - DateTime now = DateTime.UtcNow; // reference time for this request - - string chainUrl = null; - if (!httpRequest.Headers.Contains(Sdk.SIGNATURE_CERT_URL_REQUEST_HEADER) || - String.IsNullOrEmpty(chainUrl = httpRequest.Headers.GetValues(Sdk.SIGNATURE_CERT_URL_REQUEST_HEADER).First())) { - validationResult = validationResult | SpeechletRequestValidationResult.NoCertHeader; - } - - string signature = null; - if (!httpRequest.Headers.Contains(Sdk.SIGNATURE_REQUEST_HEADER) || - String.IsNullOrEmpty(signature = httpRequest.Headers.GetValues(Sdk.SIGNATURE_REQUEST_HEADER).First())) { - validationResult = validationResult | SpeechletRequestValidationResult.NoSignatureHeader; - } - - var alexaBytes = AsyncHelpers.RunSync(() => httpRequest.Content.ReadAsByteArrayAsync()); - var alexaContent = Encoding.UTF8.GetString(alexaBytes); - Debug.WriteLine(alexaContent); - - // attempt to verify signature only if we were able to locate certificate and signature headers - if (validationResult == SpeechletRequestValidationResult.OK) { - if (!SpeechletRequestSignatureVerifier.VerifyRequestSignature(alexaBytes, signature, chainUrl)) { - validationResult = validationResult | SpeechletRequestValidationResult.InvalidSignature; - } - } - - SpeechletRequestEnvelope alexaRequest = null; - try { - alexaRequest = SpeechletRequestEnvelope.FromJson(alexaContent); - } - catch (Exception ex) - when (ex is Newtonsoft.Json.JsonReaderException || ex is InvalidCastException || ex is FormatException) { - validationResult = validationResult | SpeechletRequestValidationResult.InvalidJson; - } - - // attempt to verify timestamp only if we were able to parse request body - if (alexaRequest != null) { - if (!SpeechletRequestTimestampVerifier.VerifyRequestTimestamp(alexaRequest, now)) { - validationResult = validationResult | SpeechletRequestValidationResult.InvalidTimestamp; - } - } - - if (alexaRequest == null || !OnRequestValidation(validationResult, now, alexaRequest)) { - return new HttpResponseMessage(HttpStatusCode.BadRequest) { - ReasonPhrase = validationResult.ToString() - }; - } - - string alexaResponse = DoProcessRequest(alexaRequest); - Debug.WriteLine(alexaResponse); - - HttpResponseMessage httpResponse; - if (alexaResponse == null) { - httpResponse = new HttpResponseMessage(HttpStatusCode.InternalServerError); - } - else { - httpResponse = new HttpResponseMessage(HttpStatusCode.OK); - httpResponse.Content = new StringContent(alexaResponse, Encoding.UTF8, "application/json"); - } - - return httpResponse; + return (this as ISpeechlet).GetResponse(httpRequest); } @@ -90,8 +27,7 @@ public virtual HttpResponseMessage GetResponse(HttpRequestMessage httpRequest) { /// /// public virtual string ProcessRequest(string requestContent) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); - return DoProcessRequest(requestEnvelope); + return (this as ISpeechlet).ProcessRequest(requestContent); } @@ -101,94 +37,7 @@ public virtual string ProcessRequest(string requestContent) { /// /// public virtual string ProcessRequest(JObject requestJson) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); - return DoProcessRequest(requestEnvelope); - } - - - /// - /// - /// - /// - /// - private string DoProcessRequest(SpeechletRequestEnvelope requestEnvelope) { - var session = requestEnvelope.Session; - var context = requestEnvelope.Context; - var request = requestEnvelope.Request; - ISpeechletResponse response = null; - - // Do session management prior to calling OnSessionStarted and OnIntentAsync - // to allow dev to change session values if behavior is not desired - DoSessionManagement(request as IntentRequest, session); - - if (requestEnvelope.Session.IsNew) { - OnSessionStarted( - new SessionStartedRequest(request.RequestId, request.Timestamp, request.Locale), session); - } - - // process launch request - if (requestEnvelope.Request is LaunchRequest) { - response = OnLaunch(request as LaunchRequest, session); - } - - // process audio player request - else if (requestEnvelope.Request is AudioPlayerRequest) { - response = OnAudioPlayer(request as AudioPlayerRequest, context); - } - - // process playback controller request - else if (requestEnvelope.Request is PlaybackControllerRequest) { - response = OnPlaybackController(request as PlaybackControllerRequest, context); - } - - // process system request - else if (requestEnvelope.Request is SystemExceptionEncounteredRequest) { - OnSystemExceptionEncountered(request as SystemExceptionEncounteredRequest, context); - } - - // process intent request - else if (requestEnvelope.Request is IntentRequest) { - response = OnIntent(request as IntentRequest, session, context); - } - - // process session ended request - else if (requestEnvelope.Request is SessionEndedRequest) { - OnSessionEnded(request as SessionEndedRequest, session); - } - - var responseEnvelope = new SpeechletResponseEnvelope { - Version = requestEnvelope.Version, - Response = response, - SessionAttributes = session.Attributes - }; - return responseEnvelope.ToJson(); - } - - - /// - /// - /// - private void DoSessionManagement(IntentRequest request, Session session) { - if (request == null) return; - - if (session.IsNew) { - session.Attributes[Session.INTENT_SEQUENCE] = request.Intent.Name; - } - else { - // if the session was started as a result of a launch request - // a first intent isn't yet set, so set it to the current intent - if (!session.Attributes.ContainsKey(Session.INTENT_SEQUENCE)) { - session.Attributes[Session.INTENT_SEQUENCE] = request.Intent.Name; - } - else { - session.Attributes[Session.INTENT_SEQUENCE] += Session.SEPARATOR + request.Intent.Name; - } - } - - // Auto-session management: copy all slot values from current intent into session - foreach (var slot in request.Intent.Slots.Values) { - session.Attributes[slot.Name] = slot.Value; - } + return (this as ISpeechlet).ProcessRequest(requestJson); } diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs index d74cd02..6e71c5c 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs @@ -1,18 +1,15 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. +using AlexaSkillsKit.Authentication; +using AlexaSkillsKit.Json; +using Newtonsoft.Json.Linq; using System; -using System.Diagnostics; -using System.Linq; -using System.Net; using System.Net.Http; -using System.Text; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; -using AlexaSkillsKit.Json; -using AlexaSkillsKit.Authentication; namespace AlexaSkillsKit.Speechlet { + [Obsolete("SpeechletAsync base class is obselete and will be removed in a future versions of this library. Implement ISpeechletAsync interface directly instead.")] public abstract class SpeechletAsync : ISpeechletAsync { /// @@ -21,67 +18,7 @@ public abstract class SpeechletAsync : ISpeechletAsync /// /// public async virtual Task GetResponseAsync(HttpRequestMessage httpRequest) { - SpeechletRequestValidationResult validationResult = SpeechletRequestValidationResult.OK; - DateTime now = DateTime.UtcNow; // reference time for this request - - string chainUrl = null; - if (!httpRequest.Headers.Contains(Sdk.SIGNATURE_CERT_URL_REQUEST_HEADER) || - String.IsNullOrEmpty(chainUrl = httpRequest.Headers.GetValues(Sdk.SIGNATURE_CERT_URL_REQUEST_HEADER).First())) { - validationResult = validationResult | SpeechletRequestValidationResult.NoCertHeader; - } - - string signature = null; - if (!httpRequest.Headers.Contains(Sdk.SIGNATURE_REQUEST_HEADER) || - String.IsNullOrEmpty(signature = httpRequest.Headers.GetValues(Sdk.SIGNATURE_REQUEST_HEADER).First())) { - validationResult = validationResult | SpeechletRequestValidationResult.NoSignatureHeader; - } - - var alexaBytes = await httpRequest.Content.ReadAsByteArrayAsync(); - var alexaContent = Encoding.UTF8.GetString(alexaBytes); - Debug.WriteLine(alexaContent); - - // attempt to verify signature only if we were able to locate certificate and signature headers - if (validationResult == SpeechletRequestValidationResult.OK) { - if (!(await SpeechletRequestSignatureVerifier.VerifyRequestSignatureAsync(alexaBytes, signature, chainUrl))) { - validationResult = validationResult | SpeechletRequestValidationResult.InvalidSignature; - } - } - - SpeechletRequestEnvelope alexaRequest = null; - try { - alexaRequest = SpeechletRequestEnvelope.FromJson(alexaContent); - } - catch (Exception ex) - when (ex is Newtonsoft.Json.JsonReaderException || ex is InvalidCastException || ex is FormatException) { - validationResult = validationResult | SpeechletRequestValidationResult.InvalidJson; - } - - // attempt to verify timestamp only if we were able to parse request body - if (alexaRequest != null) { - if (!SpeechletRequestTimestampVerifier.VerifyRequestTimestamp(alexaRequest, now)) { - validationResult = validationResult | SpeechletRequestValidationResult.InvalidTimestamp; - } - } - - if (alexaRequest == null || !await OnRequestValidationAsync(validationResult, now, alexaRequest)) { - return new HttpResponseMessage(HttpStatusCode.BadRequest) { - ReasonPhrase = validationResult.ToString() - }; - } - - string alexaResponse = await DoProcessRequestAsync(alexaRequest); - Debug.WriteLine(alexaResponse); - - HttpResponseMessage httpResponse; - if (alexaResponse == null) { - httpResponse = new HttpResponseMessage(HttpStatusCode.InternalServerError); - } - else { - httpResponse = new HttpResponseMessage(HttpStatusCode.OK); - httpResponse.Content = new StringContent(alexaResponse, Encoding.UTF8, "application/json"); - } - - return httpResponse; + return await (this as ISpeechletAsync).GetResponseAsync(httpRequest); } @@ -91,8 +28,7 @@ public async virtual Task GetResponseAsync(HttpRequestMessa /// /// public async virtual Task ProcessRequestAsync(string requestContent) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); - return await DoProcessRequestAsync(requestEnvelope); + return await (this as ISpeechletAsync).ProcessRequestAsync(requestContent); } @@ -102,94 +38,7 @@ public async virtual Task ProcessRequestAsync(string requestContent) { /// /// public async virtual Task ProcessRequestAsync(JObject requestJson) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); - return await DoProcessRequestAsync(requestEnvelope); - } - - - /// - /// - /// - /// - /// - private async Task DoProcessRequestAsync(SpeechletRequestEnvelope requestEnvelope) { - Session session = requestEnvelope.Session; - var context = requestEnvelope.Context; - var request = requestEnvelope.Request; - ISpeechletResponse response = null; - - // Do session management prior to calling OnSessionStarted and OnIntentAsync - // to allow dev to change session values if behavior is not desired - DoSessionManagement(request as IntentRequest, session); - - if (requestEnvelope.Session.IsNew) { - await OnSessionStartedAsync( - new SessionStartedRequest(request.RequestId, request.Timestamp, request.Locale), session); - } - - // process launch request - if (requestEnvelope.Request is LaunchRequest) { - response = await OnLaunchAsync(request as LaunchRequest, session); - } - - // process audio player request - else if (requestEnvelope.Request is AudioPlayerRequest) { - response = await OnAudioPlayerAsync(request as AudioPlayerRequest, context); - } - - // process playback controller request - else if (requestEnvelope.Request is PlaybackControllerRequest) { - response = await OnPlaybackControllerAsync(request as PlaybackControllerRequest, context); - } - - // process system request - else if (requestEnvelope.Request is SystemExceptionEncounteredRequest) { - await OnSystemExceptionEncounteredAsync(request as SystemExceptionEncounteredRequest, context); - } - - // process intent request - else if (requestEnvelope.Request is IntentRequest) { - response = await OnIntentAsync(request as IntentRequest, session, context); - } - - // process session ended request - else if (requestEnvelope.Request is SessionEndedRequest) { - await OnSessionEndedAsync(request as SessionEndedRequest, session); - } - - var responseEnvelope = new SpeechletResponseEnvelope { - Version = requestEnvelope.Version, - Response = response, - SessionAttributes = session.Attributes - }; - return responseEnvelope.ToJson(); - } - - - /// - /// - /// - private void DoSessionManagement(IntentRequest request, Session session) { - if (request == null) return; - - if (session.IsNew) { - session.Attributes[Session.INTENT_SEQUENCE] = request.Intent.Name; - } - else { - // if the session was started as a result of a launch request - // a first intent isn't yet set, so set it to the current intent - if (!session.Attributes.ContainsKey(Session.INTENT_SEQUENCE)) { - session.Attributes[Session.INTENT_SEQUENCE] = request.Intent.Name; - } - else { - session.Attributes[Session.INTENT_SEQUENCE] += Session.SEPARATOR + request.Intent.Name; - } - } - - // Auto-session management: copy all slot values from current intent into session - foreach (var slot in request.Intent.Slots.Values) { - if (!String.IsNullOrEmpty(slot.Value)) session.Attributes[slot.Name] = slot.Value; - } + return await (this as ISpeechletAsync).ProcessRequestAsync(requestJson); } diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs new file mode 100644 index 0000000..d5254c1 --- /dev/null +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs @@ -0,0 +1,51 @@ +// Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. + +using System; +using System.Threading.Tasks; +using AlexaSkillsKit.Json; +using AlexaSkillsKit.Authentication; + +namespace AlexaSkillsKit.Speechlet +{ + public class SpeechletAsyncWrapper: ISpeechletAsync + { + private readonly ISpeechlet speechlet; + + public SpeechletAsyncWrapper(ISpeechlet speechlet) { + this.speechlet = speechlet; + } + + public async Task OnAudioPlayerAsync(AudioPlayerRequest audioRequest, Context context) { + return speechlet.OnAudioPlayer(audioRequest, context); + } + + public async Task OnPlaybackControllerAsync(PlaybackControllerRequest playbackRequest, Context context) { + return speechlet.OnPlaybackController(playbackRequest, context); + } + + public async Task OnSystemExceptionEncounteredAsync(SystemExceptionEncounteredRequest systemRequest, Context context) { + speechlet.OnSystemExceptionEncountered(systemRequest, context); + } + + public async Task OnIntentAsync(IntentRequest intentRequest, Session session, Context context) { + return speechlet.OnIntent(intentRequest, session, context); + } + + public async Task OnLaunchAsync(LaunchRequest launchRequest, Session session) { + return speechlet.OnLaunch(launchRequest, session); + } + + + public async Task OnSessionEndedAsync(SessionEndedRequest sessionEndedRequest, Session session) { + speechlet.OnSessionEnded(sessionEndedRequest, session); + } + + public async Task OnSessionStartedAsync(SessionStartedRequest sessionStartedRequest, Session session) { + speechlet.OnSessionStarted(sessionStartedRequest, session); + } + + public async Task OnRequestValidationAsync(SpeechletRequestValidationResult result, DateTime referenceTimeUtc, SpeechletRequestEnvelope requestEnvelope) { + return speechlet.OnRequestValidation(result, referenceTimeUtc, requestEnvelope); + } + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletExtensions.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletExtensions.cs new file mode 100644 index 0000000..a82873a --- /dev/null +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletExtensions.cs @@ -0,0 +1,99 @@ +// Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. + +using AlexaSkillsKit.Json; +using Newtonsoft.Json.Linq; +using System.Net.Http; +using System.Threading.Tasks; + +namespace AlexaSkillsKit.Speechlet +{ + public static class SpeechletExtensions + { + /// + /// Processes Alexa request but does NOT validate request signature + /// + /// + /// + public static string ProcessRequest(this ISpeechlet speechlet, string requestContent) { + var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); + return speechlet.ProcessRequest(requestEnvelope)?.ToJson(); + } + + public static string ProcessRequest(this ISpeechletAsync speechletAsync, string requestContent) { + var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); + return speechletAsync.ProcessRequest(requestEnvelope)?.ToJson(); + } + + public static async Task ProcessRequestAsync(this ISpeechlet speechlet, string requestContent) { + var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); + return (await speechlet.ProcessRequestAsync(requestEnvelope))?.ToJson(); + } + + public static async Task ProcessRequestAsync(this ISpeechletAsync speechletAsync, string requestContent) { + var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); + return (await speechletAsync.ProcessRequestAsync(requestEnvelope))?.ToJson(); + } + + public static string ProcessRequest(this ISpeechlet speechlet, JObject requestJson) { + var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); + return speechlet.ProcessRequest(requestEnvelope)?.ToJson(); + } + + public static string ProcessRequest(this ISpeechletAsync speechletAsync, JObject requestJson) { + var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); + return speechletAsync.ProcessRequest(requestEnvelope)?.ToJson(); + } + + public static async Task ProcessRequestAsync(this ISpeechlet speechlet, JObject requestJson) { + var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); + return (await speechlet.ProcessRequestAsync(requestEnvelope))?.ToJson(); + } + + public static async Task ProcessRequestAsync(this ISpeechletAsync speechletAsync, JObject requestJson) { + var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); + return (await speechletAsync.ProcessRequestAsync(requestEnvelope))?.ToJson(); + } + + /// + /// Processes Alexa request but does NOT validate request signature + /// + /// + /// + public static SpeechletResponseEnvelope ProcessRequest(this ISpeechlet speechlet, SpeechletRequestEnvelope requestEnvelope) { + return AsyncHelpers.RunSync(() => speechlet.ProcessRequestAsync(requestEnvelope)); + } + + public static SpeechletResponseEnvelope ProcessRequest(this ISpeechletAsync speechletAsync, SpeechletRequestEnvelope requestEnvelope) { + return AsyncHelpers.RunSync(() => speechletAsync.ProcessRequestAsync(requestEnvelope)); + } + + public static async Task ProcessRequestAsync(this ISpeechlet speechlet, SpeechletRequestEnvelope requestEnvelope) { + return await new SpeechletRequestHandler(speechlet).DoProcessRequestAsync(requestEnvelope); + } + + public static async Task ProcessRequestAsync(this ISpeechletAsync speechletAsync, SpeechletRequestEnvelope requestEnvelope) { + return await new SpeechletRequestHandler(speechletAsync).DoProcessRequestAsync(requestEnvelope); + } + + /// + /// Processes Alexa request AND validates request signature + /// + /// + /// + public static HttpResponseMessage GetResponse(this ISpeechlet speechlet, HttpRequestMessage httpRequest) { + return AsyncHelpers.RunSync(() => speechlet.GetResponseAsync(httpRequest)); + } + + public static HttpResponseMessage GetResponse(this ISpeechletAsync speechletAsync, HttpRequestMessage httpRequest) { + return AsyncHelpers.RunSync(() => speechletAsync.GetResponseAsync(httpRequest)); + } + + public static async Task GetResponseAsync(this ISpeechlet speechlet, HttpRequestMessage httpRequest) { + return await new SpeechletRequestHandler(speechlet).GetResponseAsync(httpRequest); + } + + public static async Task GetResponseAsync(this ISpeechletAsync speechletAsync, HttpRequestMessage httpRequest) { + return await new SpeechletRequestHandler(speechletAsync).GetResponseAsync(httpRequest); + } + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletRequestHandler.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletRequestHandler.cs index 0860fd8..9a490e7 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletRequestHandler.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletRequestHandler.cs @@ -7,49 +7,24 @@ using System.Net.Http; using System.Text; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using AlexaSkillsKit.Json; using AlexaSkillsKit.Authentication; +using Newtonsoft.Json; namespace AlexaSkillsKit.Speechlet { public class SpeechletRequestHandler { - private readonly ISpeechlet speechlet; private readonly ISpeechletAsync speechletAsync; public SpeechletRequestHandler(ISpeechlet speechlet) { - this.speechlet = speechlet; + speechletAsync = new SpeechletAsyncWrapper(speechlet); } public SpeechletRequestHandler(ISpeechletAsync speechletAsync) { this.speechletAsync = speechletAsync; } - public HttpResponseMessage GetResponse(HttpRequestMessage httpRequest) { - return AsyncHelpers.RunSync(() => GetResponseAsync(httpRequest)); - } - - /// - /// Processes Alexa request but does NOT validate request signature - /// - /// - /// - public string ProcessRequest(string requestContent) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); - return AsyncHelpers.RunSync(() => DoProcessRequestAsync(requestEnvelope)); - } - - - /// - /// Processes Alexa request but does NOT validate request signature - /// - /// - /// - public string ProcessRequest(JObject requestJson) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); - return AsyncHelpers.RunSync(() => DoProcessRequestAsync(requestEnvelope)); - } /// /// Processes Alexa request AND validates request signature @@ -58,7 +33,6 @@ public string ProcessRequest(JObject requestJson) { /// public async virtual Task GetResponseAsync(HttpRequestMessage httpRequest) { SpeechletRequestValidationResult validationResult = SpeechletRequestValidationResult.OK; - DateTime now = DateTime.UtcNow; // reference time for this request string chainUrl = null; if (!httpRequest.Headers.Contains(Sdk.SIGNATURE_CERT_URL_REQUEST_HEADER) || @@ -88,10 +62,12 @@ public async virtual Task GetResponseAsync(HttpRequestMessa alexaRequest = SpeechletRequestEnvelope.FromJson(alexaContent); } catch (Exception ex) - when (ex is Newtonsoft.Json.JsonReaderException || ex is InvalidCastException || ex is FormatException) { + when (ex is JsonReaderException || ex is InvalidCastException || ex is FormatException) { validationResult = validationResult | SpeechletRequestValidationResult.InvalidJson; } + DateTime now = DateTime.UtcNow; // reference time for this request + // attempt to verify timestamp only if we were able to parse request body if (alexaRequest != null) { if (!SpeechletRequestTimestampVerifier.VerifyRequestTimestamp(alexaRequest, now)) { @@ -99,48 +75,21 @@ public async virtual Task GetResponseAsync(HttpRequestMessa } } - if (alexaRequest == null || !await OnRequestValidationAsync(validationResult, now, alexaRequest)) { + if (alexaRequest == null || !await speechletAsync.OnRequestValidationAsync(validationResult, now, alexaRequest)) { return new HttpResponseMessage(HttpStatusCode.BadRequest) { ReasonPhrase = validationResult.ToString() }; } - string alexaResponse = await DoProcessRequestAsync(alexaRequest); - Debug.WriteLine(alexaResponse); + var alexaResponse = await DoProcessRequestAsync(alexaRequest); + var json = alexaResponse?.ToJson(); + Debug.WriteLine(json); - HttpResponseMessage httpResponse; - if (alexaResponse == null) { - httpResponse = new HttpResponseMessage(HttpStatusCode.InternalServerError); - } - else { - httpResponse = new HttpResponseMessage(HttpStatusCode.OK) { - Content = new StringContent(alexaResponse, Encoding.UTF8, "application/json") + return (alexaResponse == null) ? + new HttpResponseMessage(HttpStatusCode.InternalServerError) : + new HttpResponseMessage(HttpStatusCode.OK) { + Content = new StringContent(json, Encoding.UTF8, "application/json") }; - } - - return httpResponse; - } - - - /// - /// Processes Alexa request but does NOT validate request signature - /// - /// - /// - public async virtual Task ProcessRequestAsync(string requestContent) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); - return await DoProcessRequestAsync(requestEnvelope); - } - - - /// - /// Processes Alexa request but does NOT validate request signature - /// - /// - /// - public async virtual Task ProcessRequestAsync(JObject requestJson) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); - return await DoProcessRequestAsync(requestEnvelope); } @@ -149,7 +98,7 @@ public async virtual Task ProcessRequestAsync(JObject requestJson) { /// /// /// - private async Task DoProcessRequestAsync(SpeechletRequestEnvelope requestEnvelope) { + public async Task DoProcessRequestAsync(SpeechletRequestEnvelope requestEnvelope) { Session session = requestEnvelope.Session; var context = requestEnvelope.Context; var request = requestEnvelope.Request; @@ -160,38 +109,38 @@ private async Task DoProcessRequestAsync(SpeechletRequestEnvelope reques DoSessionManagement(request as IntentRequest, session); if (requestEnvelope.Session.IsNew) { - await OnSessionStartedAsync( + await speechletAsync.OnSessionStartedAsync( new SessionStartedRequest(request.RequestId, request.Timestamp, request.Locale), session); } // process launch request if (requestEnvelope.Request is LaunchRequest) { - response = await OnLaunchAsync(request as LaunchRequest, session); + response = await speechletAsync.OnLaunchAsync(request as LaunchRequest, session); } // process audio player request else if (requestEnvelope.Request is AudioPlayerRequest) { - response = await OnAudioPlayerAsync(request as AudioPlayerRequest, context); + response = await speechletAsync.OnAudioPlayerAsync(request as AudioPlayerRequest, context); } // process playback controller request else if (requestEnvelope.Request is PlaybackControllerRequest) { - response = await OnPlaybackControllerAsync(request as PlaybackControllerRequest, context); + response = await speechletAsync.OnPlaybackControllerAsync(request as PlaybackControllerRequest, context); } // process system request else if (requestEnvelope.Request is SystemExceptionEncounteredRequest) { - await OnSystemExceptionEncounteredAsync(request as SystemExceptionEncounteredRequest, context); + await speechletAsync.OnSystemExceptionEncounteredAsync(request as SystemExceptionEncounteredRequest, context); } // process intent request else if (requestEnvelope.Request is IntentRequest) { - response = await OnIntentAsync(request as IntentRequest, session, context); + response = await speechletAsync.OnIntentAsync(request as IntentRequest, session, context); } // process session ended request else if (requestEnvelope.Request is SessionEndedRequest) { - await OnSessionEndedAsync(request as SessionEndedRequest, session); + await speechletAsync.OnSessionEndedAsync(request as SessionEndedRequest, session); } var responseEnvelope = new SpeechletResponseEnvelope { @@ -199,7 +148,8 @@ await OnSessionStartedAsync( Response = response, SessionAttributes = session.Attributes }; - return responseEnvelope.ToJson(); + + return responseEnvelope; } @@ -228,62 +178,5 @@ private void DoSessionManagement(IntentRequest request, Session session) { if (!String.IsNullOrEmpty(slot.Value)) session.Attributes[slot.Name] = slot.Value; } } - - private async Task OnAudioPlayerAsync(AudioPlayerRequest audioRequest, Context context) { - return speechlet != null ? - speechlet.OnAudioPlayer(audioRequest, context) - : - await speechletAsync.OnAudioPlayerAsync(audioRequest, context); - } - - private async Task OnPlaybackControllerAsync(PlaybackControllerRequest playbackRequest, Context context) { - return speechlet is ISpeechlet ? - (speechlet as ISpeechlet).OnPlaybackController(playbackRequest, context) - : - await (speechlet as ISpeechletAsync).OnPlaybackControllerAsync(playbackRequest, context); - } - - private async Task OnSystemExceptionEncounteredAsync(SystemExceptionEncounteredRequest systemRequest, Context context) { - if (speechlet is ISpeechlet) - (speechlet as ISpeechlet).OnSystemExceptionEncountered(systemRequest, context); - else - await (speechlet as ISpeechletAsync).OnSystemExceptionEncounteredAsync(systemRequest, context); - } - - private async Task OnIntentAsync(IntentRequest intentRequest, Session session, Context context) { - return speechlet is ISpeechlet ? - (speechlet as ISpeechlet).OnIntent(intentRequest, session, context) - : - await (speechlet as ISpeechletAsync).OnIntentAsync(intentRequest, session, context); - } - - private async Task OnLaunchAsync(LaunchRequest launchRequest, Session session) { - return speechlet is ISpeechlet ? - (speechlet as ISpeechlet).OnLaunch(launchRequest, session) - : - await (speechlet as ISpeechletAsync).OnLaunchAsync(launchRequest, session); - } - - - private async Task OnSessionEndedAsync(SessionEndedRequest sessionEndedRequest, Session session) { - if (speechlet is ISpeechlet) - (speechlet as ISpeechlet).OnSessionEnded(sessionEndedRequest, session); - else - await (speechlet as ISpeechletAsync).OnSessionEndedAsync(sessionEndedRequest, session); - } - - private async Task OnSessionStartedAsync(SessionStartedRequest sessionStartedRequest, Session session) { - if (speechlet is ISpeechlet) - (speechlet as ISpeechlet).OnSessionStarted(sessionStartedRequest, session); - else - await (speechlet as ISpeechletAsync).OnSessionStartedAsync(sessionStartedRequest, session); - } - - private async Task OnRequestValidationAsync(SpeechletRequestValidationResult result, DateTime referenceTimeUtc, SpeechletRequestEnvelope requestEnvelope) { - return speechlet is ISpeechlet ? - (speechlet as ISpeechlet).OnRequestValidation(result, referenceTimeUtc, requestEnvelope) - : - await (speechlet as ISpeechletAsync).OnRequestValidationAsync(result, referenceTimeUtc, requestEnvelope); - } } } \ No newline at end of file From de67b22a9f9804dcb5a449661736187c2f245912 Mon Sep 17 00:00:00 2001 From: Sergey Greenko Date: Sun, 10 Dec 2017 22:19:02 +0200 Subject: [PATCH 03/16] Extensibility implementation --- AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj | 30 ++++--- .../AudioPlayerPlaybackFailedRequest.cs | 9 +-- .../AudioPlayer}/AudioPlayerRequest.cs | 9 +-- .../AudioPlayer}/AudioPlayerResponse.cs | 0 .../AudioPlayerSpeechletAsyncWrapper.cs | 21 +++++ .../AudioPlayerSpeechletExtensions.cs | 26 +++++++ .../Directives}/AudioPlayerDirective.cs | 0 .../AudioPlayer/IAudioPlayerSpeechlet.cs | 8 ++ .../AudioPlayer/IAudioPlayerSpeechletAsync.cs | 10 +++ .../AudioPlayer}/PlaybackControllerRequest.cs | 5 +- AlexaSkillsKit.Lib/Json/Deserializer.cs | 8 +- AlexaSkillsKit.Lib/Json/InterfaceResolver.cs | 29 +++++++ .../Json/SpeechletRequestEnvelope.cs | 78 +------------------ .../Json/SpeechletRequestResolver.cs | 21 +++++ AlexaSkillsKit.Lib/Speechlet/ISpeechlet.cs | 2 - .../Speechlet/ISpeechletAsync.cs | 2 - .../ExtendedSpeechletRequest.cs | 5 +- .../Speechlet/{ => Requests}/IntentRequest.cs | 11 ++- .../Speechlet/{ => Requests}/LaunchRequest.cs | 5 +- .../{ => Requests}/SessionEndedRequest.cs | 10 ++- .../{ => Requests}/SessionStartedRequest.cs | 5 +- .../Speechlet/Requests/SpeechletRequest.cs | 55 +++++++++++++ .../SystemExceptionEncounteredRequest.cs | 9 +-- AlexaSkillsKit.Lib/Speechlet/Speechlet.cs | 2 - .../Speechlet/SpeechletAsync.cs | 2 - .../Speechlet/SpeechletAsyncWrapper.cs | 12 +-- .../Speechlet/SpeechletExtensions.cs | 54 ++++++++----- .../Speechlet/SpeechletRequest.cs | 30 ------- .../Speechlet/SpeechletRequestHandler.cs | 62 +++++++-------- .../Speechlet/AlexaController.cs | 9 +-- .../Speechlet/SampleSessionSpeechlet.cs | 8 -- 31 files changed, 298 insertions(+), 239 deletions(-) rename AlexaSkillsKit.Lib/{Speechlet => Interfaces/AudioPlayer}/AudioPlayerPlaybackFailedRequest.cs (56%) rename AlexaSkillsKit.Lib/{Speechlet => Interfaces/AudioPlayer}/AudioPlayerRequest.cs (59%) rename AlexaSkillsKit.Lib/{Speechlet => Interfaces/AudioPlayer}/AudioPlayerResponse.cs (100%) create mode 100644 AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerSpeechletAsyncWrapper.cs create mode 100644 AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerSpeechletExtensions.cs rename AlexaSkillsKit.Lib/{Speechlet => Interfaces/AudioPlayer/Directives}/AudioPlayerDirective.cs (100%) create mode 100644 AlexaSkillsKit.Lib/Interfaces/AudioPlayer/IAudioPlayerSpeechlet.cs create mode 100644 AlexaSkillsKit.Lib/Interfaces/AudioPlayer/IAudioPlayerSpeechletAsync.cs rename AlexaSkillsKit.Lib/{Speechlet => Interfaces/AudioPlayer}/PlaybackControllerRequest.cs (59%) create mode 100644 AlexaSkillsKit.Lib/Json/InterfaceResolver.cs create mode 100644 AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs rename AlexaSkillsKit.Lib/Speechlet/{ => Requests}/ExtendedSpeechletRequest.cs (77%) rename AlexaSkillsKit.Lib/Speechlet/{ => Requests}/IntentRequest.cs (59%) rename AlexaSkillsKit.Lib/Speechlet/{ => Requests}/LaunchRequest.cs (57%) rename AlexaSkillsKit.Lib/Speechlet/{ => Requests}/SessionEndedRequest.cs (58%) rename AlexaSkillsKit.Lib/Speechlet/{ => Requests}/SessionStartedRequest.cs (57%) create mode 100644 AlexaSkillsKit.Lib/Speechlet/Requests/SpeechletRequest.cs rename AlexaSkillsKit.Lib/Speechlet/{ => Requests}/SystemExceptionEncounteredRequest.cs (62%) delete mode 100644 AlexaSkillsKit.Lib/Speechlet/SpeechletRequest.cs diff --git a/AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj b/AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj index 5863eee..9984b39 100644 --- a/AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj +++ b/AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj @@ -63,6 +63,9 @@ + + + @@ -77,47 +80,50 @@ + + + - + - - + + - - - - + + + + - + - + - - + + - + diff --git a/AlexaSkillsKit.Lib/Speechlet/AudioPlayerPlaybackFailedRequest.cs b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerPlaybackFailedRequest.cs similarity index 56% rename from AlexaSkillsKit.Lib/Speechlet/AudioPlayerPlaybackFailedRequest.cs rename to AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerPlaybackFailedRequest.cs index 5c52f70..9685e09 100644 --- a/AlexaSkillsKit.Lib/Speechlet/AudioPlayerPlaybackFailedRequest.cs +++ b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerPlaybackFailedRequest.cs @@ -1,4 +1,4 @@ -using System; +using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { @@ -7,10 +7,9 @@ namespace AlexaSkillsKit.Speechlet /// public class AudioPlayerPlaybackFailedRequest : AudioPlayerRequest { - public AudioPlayerPlaybackFailedRequest(string requestId, DateTime timestamp, string locale, string subtype, string token, Error error, PlaybackState currentPlaybackState) - : base(requestId, timestamp, locale, subtype, token, 0) { - Error = error; - CurrentPlaybackState = currentPlaybackState; + public AudioPlayerPlaybackFailedRequest(JObject json, string subtype) : base(json, subtype) { + Error = Error.FromJson(json.Value("error")); + CurrentPlaybackState = PlaybackState.FromJson(json.Value("currentPlaybackState")); } public Error Error { diff --git a/AlexaSkillsKit.Lib/Speechlet/AudioPlayerRequest.cs b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerRequest.cs similarity index 59% rename from AlexaSkillsKit.Lib/Speechlet/AudioPlayerRequest.cs rename to AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerRequest.cs index 25a8349..2b24f6a 100644 --- a/AlexaSkillsKit.Lib/Speechlet/AudioPlayerRequest.cs +++ b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerRequest.cs @@ -1,4 +1,4 @@ -using System; +using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { @@ -7,10 +7,9 @@ namespace AlexaSkillsKit.Speechlet /// public class AudioPlayerRequest : ExtendedSpeechletRequest { - public AudioPlayerRequest(string requestId, DateTime timestamp, string locale, string subtype, string token, long offsetInMilliseconds) - : base(requestId, timestamp, locale, subtype) { - Token = token; - OffsetInMilliseconds = offsetInMilliseconds; + public AudioPlayerRequest(JObject json, string subtype) : base(json, subtype) { + Token = json.Value("token"); + OffsetInMilliseconds = json.Value("offsetInMilliseconds"); } public string Token { diff --git a/AlexaSkillsKit.Lib/Speechlet/AudioPlayerResponse.cs b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerResponse.cs similarity index 100% rename from AlexaSkillsKit.Lib/Speechlet/AudioPlayerResponse.cs rename to AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerResponse.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerSpeechletAsyncWrapper.cs b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerSpeechletAsyncWrapper.cs new file mode 100644 index 0000000..9d95802 --- /dev/null +++ b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerSpeechletAsyncWrapper.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; + +namespace AlexaSkillsKit.Speechlet +{ + public class AudioPlayerSpeechletAsyncWrapper : IAudioPlayerSpeechletAsync + { + private readonly IAudioPlayerSpeechlet speechlet; + + public AudioPlayerSpeechletAsyncWrapper(IAudioPlayerSpeechlet speechlet) { + this.speechlet = speechlet; + } + + public async Task OnAudioPlayerAsync(AudioPlayerRequest audioRequest, Context context) { + return speechlet.OnAudioPlayer(audioRequest, context); + } + + public async Task OnPlaybackControllerAsync(PlaybackControllerRequest playbackRequest, Context context) { + return speechlet.OnPlaybackController(playbackRequest, context); + } + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerSpeechletExtensions.cs b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerSpeechletExtensions.cs new file mode 100644 index 0000000..69430b9 --- /dev/null +++ b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerSpeechletExtensions.cs @@ -0,0 +1,26 @@ +namespace AlexaSkillsKit.Speechlet +{ + public static class AudioPlayerSpeechletExtensions + { + public static void UseAudioPlayer(this IAudioPlayerSpeechlet speechlet) { + UseAudioPlayer(new AudioPlayerSpeechletAsyncWrapper(speechlet)); + } + + public static void UseAudioPlayer(this IAudioPlayerSpeechletAsync speechlet) { + var audioPlayerResolver = new InterfaceResolver() + .WithDefaultDeserializer((json, subtype) => new AudioPlayerRequest(json, subtype)) + .WithDeserializer("PlaybackFailed", (json, subtype) => new AudioPlayerPlaybackFailedRequest(json, subtype)); + SpeechletRequestResolver.UseInterface("AudioPlayer", audioPlayerResolver); + + SpeechletRequestHandler.UseInterface( + async (request, context) => await speechlet.OnAudioPlayerAsync(request, context)); + + var playbackControllerResolver = new InterfaceResolver() + .WithDefaultDeserializer((json, subtype) => new PlaybackControllerRequest(json, subtype)); + SpeechletRequestResolver.UseInterface("PlaybackController", playbackControllerResolver); + + SpeechletRequestHandler.UseInterface( + async (request, context) => await speechlet.OnPlaybackControllerAsync(request, context)); + } + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/AudioPlayerDirective.cs b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerDirective.cs similarity index 100% rename from AlexaSkillsKit.Lib/Speechlet/AudioPlayerDirective.cs rename to AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerDirective.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/IAudioPlayerSpeechlet.cs b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/IAudioPlayerSpeechlet.cs new file mode 100644 index 0000000..dbce928 --- /dev/null +++ b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/IAudioPlayerSpeechlet.cs @@ -0,0 +1,8 @@ +namespace AlexaSkillsKit.Speechlet +{ + public interface IAudioPlayerSpeechlet + { + AudioPlayerResponse OnAudioPlayer(AudioPlayerRequest audioRequest, Context context); + AudioPlayerResponse OnPlaybackController(PlaybackControllerRequest playbackRequest, Context context); + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/IAudioPlayerSpeechletAsync.cs b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/IAudioPlayerSpeechletAsync.cs new file mode 100644 index 0000000..fa0ad60 --- /dev/null +++ b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/IAudioPlayerSpeechletAsync.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; + +namespace AlexaSkillsKit.Speechlet +{ + public interface IAudioPlayerSpeechletAsync + { + Task OnAudioPlayerAsync(AudioPlayerRequest audioRequest, Context context); + Task OnPlaybackControllerAsync(PlaybackControllerRequest playbackRequest, Context context); + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/PlaybackControllerRequest.cs b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/PlaybackControllerRequest.cs similarity index 59% rename from AlexaSkillsKit.Lib/Speechlet/PlaybackControllerRequest.cs rename to AlexaSkillsKit.Lib/Interfaces/AudioPlayer/PlaybackControllerRequest.cs index b5decf0..453dc75 100644 --- a/AlexaSkillsKit.Lib/Speechlet/PlaybackControllerRequest.cs +++ b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/PlaybackControllerRequest.cs @@ -1,4 +1,4 @@ -using System; +using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { @@ -7,8 +7,7 @@ namespace AlexaSkillsKit.Speechlet /// public class PlaybackControllerRequest : ExtendedSpeechletRequest { - public PlaybackControllerRequest(string requestId, DateTime timestamp, string locale, string subtype) - : base(requestId, timestamp, locale, subtype) { + public PlaybackControllerRequest(JObject json, string subtype) : base(json, subtype) { } } } \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Json/Deserializer.cs b/AlexaSkillsKit.Lib/Json/Deserializer.cs index f40e79f..766ec4d 100644 --- a/AlexaSkillsKit.Lib/Json/Deserializer.cs +++ b/AlexaSkillsKit.Lib/Json/Deserializer.cs @@ -17,5 +17,11 @@ public static T FromJson(JProperty json) { return deserializers[json.Name](json.Value as JObject); } + + public static T FromJson(string name, JObject json) { + if (json == null || !deserializers.ContainsKey(name)) return default(T); + + return deserializers[name](json); + } } -} +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Json/InterfaceResolver.cs b/AlexaSkillsKit.Lib/Json/InterfaceResolver.cs new file mode 100644 index 0000000..b61add3 --- /dev/null +++ b/AlexaSkillsKit.Lib/Json/InterfaceResolver.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; + +namespace AlexaSkillsKit.Speechlet +{ + public class InterfaceResolver + { + private IDictionary> deserializers + = new Dictionary>(); + + public InterfaceResolver WithDeserializer(string type, Func fromJson) { + deserializers.Add(type, fromJson); + return this; + } + + public InterfaceResolver WithDefaultDeserializer(Func fromJson) { + deserializers.Add(string.Empty, fromJson); + return this; + } + + public SpeechletRequest FromJson(string type, JObject json) { + if (json == null) return null; + if (deserializers.ContainsKey(type)) return deserializers[type](json, type); + if (deserializers.ContainsKey(string.Empty)) return deserializers[string.Empty](json, type); + return null; + } + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs b/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs index 0a61404..0c6c260 100644 --- a/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs +++ b/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs @@ -3,9 +3,7 @@ using System; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using AlexaSkillsKit.Helpers; using AlexaSkillsKit.Speechlet; -using AlexaSkillsKit.Slu; namespace AlexaSkillsKit.Json { @@ -25,84 +23,16 @@ public static SpeechletRequestEnvelope FromJson(string content) { return FromJson(json); } - - /// - /// - /// - /// - /// public static SpeechletRequestEnvelope FromJson(JObject json) { - if (json["version"] != null && json["version"].Value() != Sdk.VERSION) { + var version = json.Value("version"); + if (version != null && version != Sdk.VERSION) { throw new SpeechletException("Request must conform to 1.0 schema."); } - SpeechletRequest request; - var requestJson = json.Value("request"); - var requestTypeParts = requestJson?.Value("type")?.Split('.'); - if (requestTypeParts == null) { - throw new ArgumentException("json"); - } - - var requestType = requestTypeParts[0]; - var requestSubType = requestTypeParts.Length > 1 ? requestTypeParts[1] : null; - - var requestId = requestJson?.Value("requestId"); - var timestamp = DateTimeHelpers.FromAlexaTimestamp(requestJson); - var locale = requestJson?.Value("locale"); - switch (requestType) { - case "LaunchRequest": - request = new LaunchRequest(requestId, timestamp, locale); - break; - case "IntentRequest": - IntentRequest.DialogStateEnum dialogState = IntentRequest.DialogStateEnum.NONE; - Enum.TryParse(requestJson.Value("dialogState"), out dialogState); - var intent = Intent.FromJson(requestJson.Value("intent")); - request = new IntentRequest(requestId, timestamp, locale, intent, dialogState); - break; - case "SessionStartedRequest": - request = new SessionStartedRequest(requestId, timestamp, locale); - break; - case "SessionEndedRequest": - SessionEndedRequest.ReasonEnum reason = SessionEndedRequest.ReasonEnum.NONE; - Enum.TryParse(requestJson.Value("reason"), out reason); - request = new SessionEndedRequest(requestId, timestamp, locale, reason); - break; - case "AudioPlayer": - var token = requestJson?.Value("token"); - var offset = requestJson?.Value("offsetInMilliseconds") ?? 0; - var playbackError = Error.FromJson(requestJson?.Value("error")); - var currentPlaybackState = PlaybackState.FromJson(requestJson?.Value("currentPlaybackState")); - switch (requestSubType) { - case "PlaybackFailed": - request = new AudioPlayerPlaybackFailedRequest(requestId, timestamp, locale, requestSubType, token, playbackError, currentPlaybackState); - break; - default: - request = new AudioPlayerRequest(requestId, timestamp, locale, requestSubType, token, offset); - break; - } - break; - case "PlaybackController": - request = new PlaybackControllerRequest(requestId, timestamp, locale, requestType); - break; - case "System": - switch (requestSubType) { - case "ExceptionEncountered": - var error = Error.FromJson(requestJson?.Value("error")); - var cause = Cause.FromJson(requestJson?.Value("cause")); - request = new SystemExceptionEncounteredRequest(requestId, timestamp, locale, requestSubType, error, cause); - break; - default: - throw new ArgumentException("json"); - } - break; - default: - throw new ArgumentException("json"); - } - return new SpeechletRequestEnvelope { - Request = request, + Request = SpeechletRequest.FromJson(json.Value("request")), Session = Session.FromJson(json.Value("session")), - Version = json.Value("version"), + Version = version, Context = Context.FromJson(json.Value("context")) }; } diff --git a/AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs b/AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs new file mode 100644 index 0000000..b27f283 --- /dev/null +++ b/AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json.Linq; +using System.Collections.Generic; + +namespace AlexaSkillsKit.Speechlet +{ + public static class SpeechletRequestResolver + { + private static IDictionary resolvers + = new Dictionary(); + + public static SpeechletRequest FromJson(string type, string subtype, JObject json) { + if (json == null || !resolvers.ContainsKey(type)) return null; + + return resolvers[type].FromJson(subtype, json); + } + + public static void UseInterface(string name, InterfaceResolver resolver) { + resolvers[name] = resolver; + } + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/ISpeechlet.cs b/AlexaSkillsKit.Lib/Speechlet/ISpeechlet.cs index 1ac8979..92960df 100644 --- a/AlexaSkillsKit.Lib/Speechlet/ISpeechlet.cs +++ b/AlexaSkillsKit.Lib/Speechlet/ISpeechlet.cs @@ -11,8 +11,6 @@ public interface ISpeechlet bool OnRequestValidation( SpeechletRequestValidationResult result, DateTime referenceTimeUtc, SpeechletRequestEnvelope requestEnvelope); - AudioPlayerResponse OnAudioPlayer(AudioPlayerRequest audioRequest, Context context); - AudioPlayerResponse OnPlaybackController(PlaybackControllerRequest playbackRequest, Context context); void OnSystemExceptionEncountered(SystemExceptionEncounteredRequest systemRequest, Context context); SpeechletResponse OnIntent(IntentRequest intentRequest, Session session, Context context); diff --git a/AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs b/AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs index 93cbd97..23d9115 100644 --- a/AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs +++ b/AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs @@ -12,8 +12,6 @@ public interface ISpeechletAsync Task OnRequestValidationAsync( SpeechletRequestValidationResult result, DateTime referenceTimeUtc, SpeechletRequestEnvelope requestEnvelope); - Task OnAudioPlayerAsync(AudioPlayerRequest audioRequest, Context context); - Task OnPlaybackControllerAsync(PlaybackControllerRequest playbackRequest, Context context); Task OnSystemExceptionEncounteredAsync(SystemExceptionEncounteredRequest systemRequest, Context context); Task OnIntentAsync(IntentRequest intentRequest, Session session, Context context); diff --git a/AlexaSkillsKit.Lib/Speechlet/ExtendedSpeechletRequest.cs b/AlexaSkillsKit.Lib/Speechlet/Requests/ExtendedSpeechletRequest.cs similarity index 77% rename from AlexaSkillsKit.Lib/Speechlet/ExtendedSpeechletRequest.cs rename to AlexaSkillsKit.Lib/Speechlet/Requests/ExtendedSpeechletRequest.cs index ca5cbb3..915e250 100644 --- a/AlexaSkillsKit.Lib/Speechlet/ExtendedSpeechletRequest.cs +++ b/AlexaSkillsKit.Lib/Speechlet/Requests/ExtendedSpeechletRequest.cs @@ -1,4 +1,4 @@ -using System; +using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { @@ -9,8 +9,7 @@ namespace AlexaSkillsKit.Speechlet /// public class ExtendedSpeechletRequest : SpeechletRequest { - public ExtendedSpeechletRequest(string requestId, DateTime timestamp, string locale, string subtype) - : base(requestId, timestamp, locale) { + public ExtendedSpeechletRequest(JObject json, string subtype) : base(json) { Subtype = subtype; } diff --git a/AlexaSkillsKit.Lib/Speechlet/IntentRequest.cs b/AlexaSkillsKit.Lib/Speechlet/Requests/IntentRequest.cs similarity index 59% rename from AlexaSkillsKit.Lib/Speechlet/IntentRequest.cs rename to AlexaSkillsKit.Lib/Speechlet/Requests/IntentRequest.cs index fff550a..5d184b2 100644 --- a/AlexaSkillsKit.Lib/Speechlet/IntentRequest.cs +++ b/AlexaSkillsKit.Lib/Speechlet/Requests/IntentRequest.cs @@ -2,15 +2,18 @@ using System; using AlexaSkillsKit.Slu; +using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { public class IntentRequest : SpeechletRequest { - public IntentRequest(string requestId, DateTime timestamp, string locale, Intent intent, DialogStateEnum dialogState) - : base(requestId, timestamp, locale) { + public IntentRequest(JObject json) : base(json) { + Intent = Intent.FromJson(json.Value("intent")); - Intent = intent; + DialogStateEnum dialogState = DialogStateEnum.UNKNOWN; + Enum.TryParse(json.Value("dialogState"), out dialogState); + DialogState = dialogState; } public virtual Intent Intent { @@ -26,7 +29,7 @@ public virtual DialogStateEnum DialogState public enum DialogStateEnum { - NONE = 0, // default in case parsing fails + UNKNOWN = 0, // default in case parsing fails STARTED, IN_PROGRESS, COMPLETED diff --git a/AlexaSkillsKit.Lib/Speechlet/LaunchRequest.cs b/AlexaSkillsKit.Lib/Speechlet/Requests/LaunchRequest.cs similarity index 57% rename from AlexaSkillsKit.Lib/Speechlet/LaunchRequest.cs rename to AlexaSkillsKit.Lib/Speechlet/Requests/LaunchRequest.cs index 9a0e279..007bfa7 100644 --- a/AlexaSkillsKit.Lib/Speechlet/LaunchRequest.cs +++ b/AlexaSkillsKit.Lib/Speechlet/Requests/LaunchRequest.cs @@ -1,13 +1,12 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using System; +using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { public class LaunchRequest : SpeechletRequest { - public LaunchRequest(string requestId, DateTime timestamp, string locale) - : base(requestId, timestamp, locale) { + public LaunchRequest(JObject json) : base(json) { } } } \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/SessionEndedRequest.cs b/AlexaSkillsKit.Lib/Speechlet/Requests/SessionEndedRequest.cs similarity index 58% rename from AlexaSkillsKit.Lib/Speechlet/SessionEndedRequest.cs rename to AlexaSkillsKit.Lib/Speechlet/Requests/SessionEndedRequest.cs index 5ead55e..ac02d5c 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SessionEndedRequest.cs +++ b/AlexaSkillsKit.Lib/Speechlet/Requests/SessionEndedRequest.cs @@ -1,25 +1,27 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. +using Newtonsoft.Json.Linq; using System; namespace AlexaSkillsKit.Speechlet { public class SessionEndedRequest : SpeechletRequest { - public SessionEndedRequest(string requestId, DateTime timestamp, string locale, SessionEndedRequest.ReasonEnum reason) - : base(requestId, timestamp, locale) { + public SessionEndedRequest(JObject json) : base(json) { + ReasonEnum reason = ReasonEnum.UNKNOWN; + Enum.TryParse(json.Value("reason"), out reason); Reason = reason; } - public virtual SessionEndedRequest.ReasonEnum Reason { + public virtual ReasonEnum Reason { get; private set; } public enum ReasonEnum { - NONE = 0, // default in case parsing fails + UNKNOWN = 0, // default in case parsing fails ERROR, USER_INITIATED, EXCEEDED_MAX_REPROMPTS, diff --git a/AlexaSkillsKit.Lib/Speechlet/SessionStartedRequest.cs b/AlexaSkillsKit.Lib/Speechlet/Requests/SessionStartedRequest.cs similarity index 57% rename from AlexaSkillsKit.Lib/Speechlet/SessionStartedRequest.cs rename to AlexaSkillsKit.Lib/Speechlet/Requests/SessionStartedRequest.cs index acc9441..5b8fdf1 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SessionStartedRequest.cs +++ b/AlexaSkillsKit.Lib/Speechlet/Requests/SessionStartedRequest.cs @@ -1,13 +1,10 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using System; - namespace AlexaSkillsKit.Speechlet { public class SessionStartedRequest : SpeechletRequest { - public SessionStartedRequest(string requestId, DateTime timestamp, string locale) - : base(requestId, timestamp, locale) { + public SessionStartedRequest(SpeechletRequest other) : base(other) { } } } \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/Requests/SpeechletRequest.cs b/AlexaSkillsKit.Lib/Speechlet/Requests/SpeechletRequest.cs new file mode 100644 index 0000000..1fd2c96 --- /dev/null +++ b/AlexaSkillsKit.Lib/Speechlet/Requests/SpeechletRequest.cs @@ -0,0 +1,55 @@ +// Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. + +using AlexaSkillsKit.Helpers; +using Newtonsoft.Json.Linq; +using System; +using System.Linq; + +namespace AlexaSkillsKit.Speechlet +{ + public abstract class SpeechletRequest + { + public static SpeechletRequest FromJson(JObject json) { + var requestTypeParts = json?.Value("type")?.Split('.'); + if (requestTypeParts == null) { + throw new ArgumentException("json"); + } + + var requestType = requestTypeParts.Length > 1 ? requestTypeParts[0] : string.Empty; + var requestSubtype = requestTypeParts.Last(); + var request = SpeechletRequestResolver.FromJson(requestType, requestSubtype, json); + if (request == null) { + throw new ArgumentException("json"); + } + + return request; + } + + protected SpeechletRequest(JObject json) { + RequestId = json.Value("requestId"); + Timestamp = DateTimeHelpers.FromAlexaTimestamp(json); + Locale = json.Value("locale"); + } + + protected SpeechletRequest(SpeechletRequest other) { + RequestId = other.RequestId; + Timestamp = other.Timestamp; + Locale = other.Locale; + } + + public string RequestId { + get; + private set; + } + + public DateTime Timestamp { + get; + private set; + } + + public string Locale { + get; + private set; + } + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/SystemExceptionEncounteredRequest.cs b/AlexaSkillsKit.Lib/Speechlet/Requests/SystemExceptionEncounteredRequest.cs similarity index 62% rename from AlexaSkillsKit.Lib/Speechlet/SystemExceptionEncounteredRequest.cs rename to AlexaSkillsKit.Lib/Speechlet/Requests/SystemExceptionEncounteredRequest.cs index 12696f8..247959d 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SystemExceptionEncounteredRequest.cs +++ b/AlexaSkillsKit.Lib/Speechlet/Requests/SystemExceptionEncounteredRequest.cs @@ -1,4 +1,4 @@ -using System; +using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet { @@ -7,10 +7,9 @@ namespace AlexaSkillsKit.Speechlet /// public class SystemExceptionEncounteredRequest : ExtendedSpeechletRequest { - public SystemExceptionEncounteredRequest(string requestId, DateTime timestamp, string locale, string subtype, Error error, Cause cause) - : base(requestId, timestamp, locale, subtype) { - Error = error; - Cause = Cause; + public SystemExceptionEncounteredRequest(JObject json, string subtype) : base(json, subtype) { + Error = Error.FromJson(json.Value("error")); + Cause = Cause.FromJson(json.Value("cause")); } public Error Error { diff --git a/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs b/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs index eb176e3..53efa0b 100644 --- a/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs +++ b/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs @@ -51,8 +51,6 @@ public virtual bool OnRequestValidation( return result == SpeechletRequestValidationResult.OK; } - public abstract AudioPlayerResponse OnAudioPlayer(AudioPlayerRequest audioRequest, Context context); - public abstract AudioPlayerResponse OnPlaybackController(PlaybackControllerRequest playbackRequest, Context context); public abstract void OnSystemExceptionEncountered(SystemExceptionEncounteredRequest systemRequest, Context context); public abstract SpeechletResponse OnIntent(IntentRequest intentRequest, Session session, Context context); diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs index 6e71c5c..2a88abd 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs @@ -53,8 +53,6 @@ public virtual Task OnRequestValidationAsync( } - public abstract Task OnAudioPlayerAsync(AudioPlayerRequest audioRequest, Context context); - public abstract Task OnPlaybackControllerAsync(PlaybackControllerRequest playbackRequest, Context context); public abstract Task OnSystemExceptionEncounteredAsync(SystemExceptionEncounteredRequest systemRequest, Context context); public abstract Task OnIntentAsync(IntentRequest intentRequest, Session session, Context context); diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs index d5254c1..bc39ddb 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs @@ -1,6 +1,4 @@ -// Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. - -using System; +using System; using System.Threading.Tasks; using AlexaSkillsKit.Json; using AlexaSkillsKit.Authentication; @@ -15,14 +13,6 @@ public SpeechletAsyncWrapper(ISpeechlet speechlet) { this.speechlet = speechlet; } - public async Task OnAudioPlayerAsync(AudioPlayerRequest audioRequest, Context context) { - return speechlet.OnAudioPlayer(audioRequest, context); - } - - public async Task OnPlaybackControllerAsync(PlaybackControllerRequest playbackRequest, Context context) { - return speechlet.OnPlaybackController(playbackRequest, context); - } - public async Task OnSystemExceptionEncounteredAsync(SystemExceptionEncounteredRequest systemRequest, Context context) { speechlet.OnSystemExceptionEncountered(systemRequest, context); } diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletExtensions.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletExtensions.cs index a82873a..53487f3 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletExtensions.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletExtensions.cs @@ -1,6 +1,4 @@ -// Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. - -using AlexaSkillsKit.Json; +using AlexaSkillsKit.Json; using Newtonsoft.Json.Linq; using System.Net.Http; using System.Threading.Tasks; @@ -9,6 +7,24 @@ namespace AlexaSkillsKit.Speechlet { public static class SpeechletExtensions { + public static void UseStandard(this ISpeechlet speechlet) { + new SpeechletAsyncWrapper(speechlet).UseStandard(); + } + + public static void UseStandard(this ISpeechletAsync speechlet) { + var standardResolver = new InterfaceResolver() + .WithDeserializer(nameof(LaunchRequest), (json, subtype) => new LaunchRequest(json)) + .WithDeserializer(nameof(IntentRequest), (json, subtype) => new IntentRequest(json)) + .WithDeserializer(nameof(SessionEndedRequest), (json, subtype) => new SessionEndedRequest(json)); + SpeechletRequestResolver.UseInterface(string.Empty, standardResolver); + + var systemResolver = new InterfaceResolver() + .WithDeserializer("ExceptionEncountered", (json, subtype) => new SystemExceptionEncounteredRequest(json, subtype)); + SpeechletRequestResolver.UseInterface("System", systemResolver); + + SpeechletRequestHandler.UseStandard(speechlet); + } + /// /// Processes Alexa request but does NOT validate request signature /// @@ -19,9 +35,9 @@ public static string ProcessRequest(this ISpeechlet speechlet, string requestCon return speechlet.ProcessRequest(requestEnvelope)?.ToJson(); } - public static string ProcessRequest(this ISpeechletAsync speechletAsync, string requestContent) { + public static string ProcessRequest(this ISpeechletAsync speechlet, string requestContent) { var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); - return speechletAsync.ProcessRequest(requestEnvelope)?.ToJson(); + return speechlet.ProcessRequest(requestEnvelope)?.ToJson(); } public static async Task ProcessRequestAsync(this ISpeechlet speechlet, string requestContent) { @@ -29,9 +45,9 @@ public static async Task ProcessRequestAsync(this ISpeechlet speechlet, return (await speechlet.ProcessRequestAsync(requestEnvelope))?.ToJson(); } - public static async Task ProcessRequestAsync(this ISpeechletAsync speechletAsync, string requestContent) { + public static async Task ProcessRequestAsync(this ISpeechletAsync speechlet, string requestContent) { var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); - return (await speechletAsync.ProcessRequestAsync(requestEnvelope))?.ToJson(); + return (await speechlet.ProcessRequestAsync(requestEnvelope))?.ToJson(); } public static string ProcessRequest(this ISpeechlet speechlet, JObject requestJson) { @@ -39,9 +55,9 @@ public static string ProcessRequest(this ISpeechlet speechlet, JObject requestJs return speechlet.ProcessRequest(requestEnvelope)?.ToJson(); } - public static string ProcessRequest(this ISpeechletAsync speechletAsync, JObject requestJson) { + public static string ProcessRequest(this ISpeechletAsync speechlet, JObject requestJson) { var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); - return speechletAsync.ProcessRequest(requestEnvelope)?.ToJson(); + return speechlet.ProcessRequest(requestEnvelope)?.ToJson(); } public static async Task ProcessRequestAsync(this ISpeechlet speechlet, JObject requestJson) { @@ -49,9 +65,9 @@ public static async Task ProcessRequestAsync(this ISpeechlet speechlet, return (await speechlet.ProcessRequestAsync(requestEnvelope))?.ToJson(); } - public static async Task ProcessRequestAsync(this ISpeechletAsync speechletAsync, JObject requestJson) { + public static async Task ProcessRequestAsync(this ISpeechletAsync speechlet, JObject requestJson) { var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); - return (await speechletAsync.ProcessRequestAsync(requestEnvelope))?.ToJson(); + return (await speechlet.ProcessRequestAsync(requestEnvelope))?.ToJson(); } /// @@ -63,16 +79,16 @@ public static SpeechletResponseEnvelope ProcessRequest(this ISpeechlet speechlet return AsyncHelpers.RunSync(() => speechlet.ProcessRequestAsync(requestEnvelope)); } - public static SpeechletResponseEnvelope ProcessRequest(this ISpeechletAsync speechletAsync, SpeechletRequestEnvelope requestEnvelope) { - return AsyncHelpers.RunSync(() => speechletAsync.ProcessRequestAsync(requestEnvelope)); + public static SpeechletResponseEnvelope ProcessRequest(this ISpeechletAsync speechlet, SpeechletRequestEnvelope requestEnvelope) { + return AsyncHelpers.RunSync(() => speechlet.ProcessRequestAsync(requestEnvelope)); } public static async Task ProcessRequestAsync(this ISpeechlet speechlet, SpeechletRequestEnvelope requestEnvelope) { return await new SpeechletRequestHandler(speechlet).DoProcessRequestAsync(requestEnvelope); } - public static async Task ProcessRequestAsync(this ISpeechletAsync speechletAsync, SpeechletRequestEnvelope requestEnvelope) { - return await new SpeechletRequestHandler(speechletAsync).DoProcessRequestAsync(requestEnvelope); + public static async Task ProcessRequestAsync(this ISpeechletAsync speechlet, SpeechletRequestEnvelope requestEnvelope) { + return await new SpeechletRequestHandler(speechlet).DoProcessRequestAsync(requestEnvelope); } /// @@ -84,16 +100,16 @@ public static HttpResponseMessage GetResponse(this ISpeechlet speechlet, HttpReq return AsyncHelpers.RunSync(() => speechlet.GetResponseAsync(httpRequest)); } - public static HttpResponseMessage GetResponse(this ISpeechletAsync speechletAsync, HttpRequestMessage httpRequest) { - return AsyncHelpers.RunSync(() => speechletAsync.GetResponseAsync(httpRequest)); + public static HttpResponseMessage GetResponse(this ISpeechletAsync speechlet, HttpRequestMessage httpRequest) { + return AsyncHelpers.RunSync(() => speechlet.GetResponseAsync(httpRequest)); } public static async Task GetResponseAsync(this ISpeechlet speechlet, HttpRequestMessage httpRequest) { return await new SpeechletRequestHandler(speechlet).GetResponseAsync(httpRequest); } - public static async Task GetResponseAsync(this ISpeechletAsync speechletAsync, HttpRequestMessage httpRequest) { - return await new SpeechletRequestHandler(speechletAsync).GetResponseAsync(httpRequest); + public static async Task GetResponseAsync(this ISpeechletAsync speechlet, HttpRequestMessage httpRequest) { + return await new SpeechletRequestHandler(speechlet).GetResponseAsync(httpRequest); } } } \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletRequest.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletRequest.cs deleted file mode 100644 index a8d7d15..0000000 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletRequest.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. - -using System; - -namespace AlexaSkillsKit.Speechlet -{ - public abstract class SpeechletRequest - { - public SpeechletRequest(string requestId, DateTime timestamp, string locale) { - RequestId = requestId; - Timestamp = timestamp; - Locale = locale; - } - - public string RequestId { - get; - private set; - } - - public DateTime Timestamp { - get; - private set; - } - - public string Locale { - get; - private set; - } - } -} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletRequestHandler.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletRequestHandler.cs index 9a490e7..ce316e2 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletRequestHandler.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletRequestHandler.cs @@ -1,6 +1,4 @@ -// Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. - -using System; +using System; using System.Diagnostics; using System.Linq; using System.Net; @@ -10,11 +8,32 @@ using AlexaSkillsKit.Json; using AlexaSkillsKit.Authentication; using Newtonsoft.Json; +using System.Collections.Generic; namespace AlexaSkillsKit.Speechlet { public class SpeechletRequestHandler { + private static IDictionary>> handlers + = new Dictionary>>(); + + public static void UseInterface(Func> handler) where T: SpeechletRequest { + handlers[typeof(T)] = async (request, session, context) => await handler(request as T, context); + } + + public static void UseStandard(ISpeechletAsync speechletAsync) { + handlers[typeof(LaunchRequest)] = async (request, session, context) => await speechletAsync.OnLaunchAsync(request as LaunchRequest, session); + handlers[typeof(IntentRequest)] = async (request, session, context) => await speechletAsync.OnIntentAsync(request as IntentRequest, session, context); + handlers[typeof(SessionEndedRequest)] = async (request, session, context) => { + await speechletAsync.OnSessionEndedAsync(request as SessionEndedRequest, session); + return null; + }; + handlers[typeof(SystemExceptionEncounteredRequest)] = async (request, session, context) => { + await speechletAsync.OnSystemExceptionEncounteredAsync(request as SystemExceptionEncounteredRequest, context); + return null; + }; + } + private readonly ISpeechletAsync speechletAsync; public SpeechletRequestHandler(ISpeechlet speechlet) { @@ -25,7 +44,6 @@ public SpeechletRequestHandler(ISpeechletAsync speechletAsync) { this.speechletAsync = speechletAsync; } - /// /// Processes Alexa request AND validates request signature /// @@ -109,38 +127,14 @@ public async Task DoProcessRequestAsync(SpeechletRequ DoSessionManagement(request as IntentRequest, session); if (requestEnvelope.Session.IsNew) { - await speechletAsync.OnSessionStartedAsync( - new SessionStartedRequest(request.RequestId, request.Timestamp, request.Locale), session); - } - - // process launch request - if (requestEnvelope.Request is LaunchRequest) { - response = await speechletAsync.OnLaunchAsync(request as LaunchRequest, session); - } - - // process audio player request - else if (requestEnvelope.Request is AudioPlayerRequest) { - response = await speechletAsync.OnAudioPlayerAsync(request as AudioPlayerRequest, context); - } - - // process playback controller request - else if (requestEnvelope.Request is PlaybackControllerRequest) { - response = await speechletAsync.OnPlaybackControllerAsync(request as PlaybackControllerRequest, context); - } - - // process system request - else if (requestEnvelope.Request is SystemExceptionEncounteredRequest) { - await speechletAsync.OnSystemExceptionEncounteredAsync(request as SystemExceptionEncounteredRequest, context); - } - - // process intent request - else if (requestEnvelope.Request is IntentRequest) { - response = await speechletAsync.OnIntentAsync(request as IntentRequest, session, context); + await speechletAsync.OnSessionStartedAsync(new SessionStartedRequest(request), session); } - // process session ended request - else if (requestEnvelope.Request is SessionEndedRequest) { - await speechletAsync.OnSessionEndedAsync(request as SessionEndedRequest, session); + foreach (var pair in handlers) { + if (pair.Key.IsAssignableFrom(request.GetType())) { + response = await pair.Value(request, session, context); + break; + } } var responseEnvelope = new SpeechletResponseEnvelope { diff --git a/AlexaSkillsKit.Sample/Speechlet/AlexaController.cs b/AlexaSkillsKit.Sample/Speechlet/AlexaController.cs index 5ada783..d3cd365 100644 --- a/AlexaSkillsKit.Sample/Speechlet/AlexaController.cs +++ b/AlexaSkillsKit.Sample/Speechlet/AlexaController.cs @@ -1,12 +1,8 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using System; -using System.Collections.Generic; -using System.Threading.Tasks; +using AlexaSkillsKit.Speechlet; using System.Net.Http; using System.Web.Http; -using Newtonsoft.Json.Linq; -using AlexaSkillsKit; namespace Sample.Controllers { @@ -16,7 +12,8 @@ public class AlexaController : ApiController [HttpPost] public HttpResponseMessage SampleSession() { var speechlet = new SampleSessionSpeechlet(); + speechlet.UseStandard(); return speechlet.GetResponse(Request); } } -} +} \ No newline at end of file diff --git a/AlexaSkillsKit.Sample/Speechlet/SampleSessionSpeechlet.cs b/AlexaSkillsKit.Sample/Speechlet/SampleSessionSpeechlet.cs index 2203c6a..9253f84 100644 --- a/AlexaSkillsKit.Sample/Speechlet/SampleSessionSpeechlet.cs +++ b/AlexaSkillsKit.Sample/Speechlet/SampleSessionSpeechlet.cs @@ -22,18 +22,10 @@ public class SampleSessionSpeechlet : Speechlet private const string NAME_SLOT = "Name"; - public override AudioPlayerResponse OnAudioPlayer(AudioPlayerRequest audioRequest, Context context) { - throw new NotImplementedException(); - } - public override void OnSystemExceptionEncountered(SystemExceptionEncounteredRequest systemRequest, Context context) { throw new NotImplementedException(); } - public override AudioPlayerResponse OnPlaybackController(PlaybackControllerRequest playbackRequest, Context context) { - throw new NotImplementedException(); - } - public override void OnSessionStarted(SessionStartedRequest request, Session session) { Log.Info("OnSessionStarted requestId={0}, sessionId={1}", request.RequestId, session.SessionId); } From 2a7e3ffe9314f79512676ddbeebdfdc3e98396f1 Mon Sep 17 00:00:00 2001 From: Sergey Greenko Date: Sun, 17 Dec 2017 01:28:23 +0200 Subject: [PATCH 04/16] Extract interfaces to separate nuget packages --- .../AlexaSkillsKit.Http.csproj | 29 +++ .../SpeechletServiceExtensions.cs | 63 +++++++ ...exaSkillsKit.Interfaces.AudioPlayer.csproj | 21 +++ .../AudioItem.cs | 0 .../AudioItemStream.cs | 0 .../AudioPlayerInterface.cs | 0 .../AudioPlayerPlaybackFailedRequest.cs | 5 +- .../AudioPlayerRequest.cs | 5 +- .../AudioPlayerResponse.cs | 6 +- .../AudioPlayerSpeechletAsyncWrapper.cs | 5 +- .../AudioPlayerSpeechletServiceExtensions.cs | 31 +++ .../AudioPlayerClearQueueDirective.cs | 4 +- .../Directives/AudioPlayerDirective.cs | 4 +- .../Directives/AudioPlayerPlayDirective.cs | 4 +- .../Directives/AudioPlayerStopDirective.cs | 4 +- .../IAudioPlayerSpeechlet.cs | 4 +- .../IAudioPlayerSpeechletAsync.cs | 5 +- .../PlaybackControllerRequest.cs | 5 +- .../AlexaSkillsKit.Interfaces.Dialog.csproj | 21 +++ .../DialogConfirmIntentDirective.cs | 0 .../Directives/DialogConfirmSlotDirective.cs | 0 .../Directives/DialogDelegateDirective.cs | 0 .../Directives/DialogDirective.cs | 0 .../Directives/DialogElicitSlotDirective.cs | 0 .../AlexaSkillsKit.Interfaces.Display.csproj | 21 +++ .../DisplayRenderTemplateDirective.cs | 0 .../Directives/HintDirective.cs | 0 .../DisplayImage.cs | 0 .../DisplayImageSource.cs | 0 .../DisplayInterface.cs | 0 .../DisplayRequest.cs | 5 +- .../DisplaySpeechletAsyncWrapper.cs | 5 +- .../DisplaySpeechletServiceExtensions.cs | 22 +++ .../DisplayTemplate.cs | 0 .../IDisplaySpeechlet.cs | 4 +- .../IDisplaySpeechletAsync.cs | 5 +- .../ListItem.cs | 0 .../TextContent.cs | 0 .../TextField.cs | 0 .../AlexaSkillsKit.Interfaces.VideoApp.csproj | 21 +++ .../Directives/VideoAppLaunchDirective.cs | 0 .../VideoItem.cs | 0 .../VideoItemMetadata.cs | 0 AlexaSkillsKit.Lib/AlexaSkillsKit.Core.csproj | 26 +++ AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj | 166 ---------------- .../SpeechletRequestSignatureVerifier.cs | 104 +++------- .../SpeechletRequestValidationResult.cs | 5 +- AlexaSkillsKit.Lib/Helpers/DateTimeHelpers.cs | 1 - .../AudioPlayerSpeechletExtensions.cs | 26 --- .../Display/DisplaySpeechletExtensions.cs | 18 -- AlexaSkillsKit.Lib/Json/Deserializer.cs | 2 +- AlexaSkillsKit.Lib/Json/InterfaceResolver.cs | 5 +- .../Json/SpeechletRequestEnvelope.cs | 6 +- .../Json/SpeechletRequestResolver.cs | 21 ++- AlexaSkillsKit.Lib/Properties/AssemblyInfo.cs | 36 ---- .../Requests/ExtendedSpeechletRequest.cs | 2 +- .../{Speechlet => }/Requests/IntentRequest.cs | 2 +- .../{Speechlet => }/Requests/LaunchRequest.cs | 2 +- .../Requests/SessionEndedRequest.cs | 2 +- .../Requests/SessionStartedRequest.cs | 2 +- .../Requests/SpeechletRequest.cs | 3 +- .../SystemExceptionEncounteredRequest.cs | 5 +- AlexaSkillsKit.Lib/Sdk.cs | 1 - AlexaSkillsKit.Lib/Speechlet/Application.cs | 1 - .../{ => Speechlet}/AsyncHelpers.cs | 0 .../AudioPlayerState.cs | 0 AlexaSkillsKit.Lib/Speechlet/ISpeechlet.cs | 9 +- .../Speechlet/ISpeechletAsync.cs | 9 +- .../Speechlet/ISystemSpeechlet.cs | 9 + .../Speechlet/ISystemSpeechletAsync.cs | 10 + AlexaSkillsKit.Lib/Speechlet/Speechlet.cs | 52 +---- .../Speechlet/SpeechletAsync.cs | 52 +---- .../Speechlet/SpeechletAsyncWrapper.cs | 13 +- AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs | 51 +++++ .../Speechlet/SpeechletExtensions.cs | 115 ----------- .../Speechlet/SpeechletRequestHandler.cs | 178 ------------------ .../Speechlet/SpeechletService.cs | 138 ++++++++++++++ .../Speechlet/SpeechletServiceExtensions.cs | 40 ++++ .../Speechlet/SpeechletValidationException.cs | 24 +++ .../Speechlet/SupportedInterfaces.cs | 11 +- .../Speechlet/SystemSpeechletAsyncWrapper.cs | 18 ++ AlexaSkillsKit.Lib/app.config | 11 -- AlexaSkillsKit.Lib/packages.config | 6 - .../AlexaSkillsKit.Sample.csproj | 33 +++- .../Speechlet/AlexaController.cs | 4 +- .../Speechlet/SampleSessionSpeechlet.cs | 20 +- AlexaSkillsKit.Sample/Web.config | 32 ++-- AlexaSkillsKit.Sample/packages.config | 2 +- .../AlexaSkillsKit.Tests.csproj | 11 +- .../Authentication/SignatureVerifierTests.cs | 4 +- AlexaSkillsKit.Tests/packages.config | 2 +- AlexaSkillsKit.sln | 49 ++++- 92 files changed, 771 insertions(+), 867 deletions(-) create mode 100644 AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj create mode 100644 AlexaSkillsKit.Http/SpeechletServiceExtensions.cs create mode 100644 AlexaSkillsKit.Interfaces.AudioPlayer/AlexaSkillsKit.Interfaces.AudioPlayer.csproj rename {AlexaSkillsKit.Lib/Interfaces/AudioPlayer => AlexaSkillsKit.Interfaces.AudioPlayer}/AudioItem.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/AudioPlayer => AlexaSkillsKit.Interfaces.AudioPlayer}/AudioItemStream.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/AudioPlayer => AlexaSkillsKit.Interfaces.AudioPlayer}/AudioPlayerInterface.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/AudioPlayer => AlexaSkillsKit.Interfaces.AudioPlayer}/AudioPlayerPlaybackFailedRequest.cs (86%) rename {AlexaSkillsKit.Lib/Interfaces/AudioPlayer => AlexaSkillsKit.Interfaces.AudioPlayer}/AudioPlayerRequest.cs (84%) rename {AlexaSkillsKit.Lib/Interfaces/AudioPlayer => AlexaSkillsKit.Interfaces.AudioPlayer}/AudioPlayerResponse.cs (75%) rename {AlexaSkillsKit.Lib/Interfaces/AudioPlayer => AlexaSkillsKit.Interfaces.AudioPlayer}/AudioPlayerSpeechletAsyncWrapper.cs (86%) create mode 100644 AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerSpeechletServiceExtensions.cs rename {AlexaSkillsKit.Lib/Interfaces/AudioPlayer => AlexaSkillsKit.Interfaces.AudioPlayer}/Directives/AudioPlayerClearQueueDirective.cs (84%) rename {AlexaSkillsKit.Lib/Interfaces/AudioPlayer => AlexaSkillsKit.Interfaces.AudioPlayer}/Directives/AudioPlayerDirective.cs (76%) rename {AlexaSkillsKit.Lib/Interfaces/AudioPlayer => AlexaSkillsKit.Interfaces.AudioPlayer}/Directives/AudioPlayerPlayDirective.cs (86%) rename {AlexaSkillsKit.Lib/Interfaces/AudioPlayer => AlexaSkillsKit.Interfaces.AudioPlayer}/Directives/AudioPlayerStopDirective.cs (75%) rename {AlexaSkillsKit.Lib/Interfaces/AudioPlayer => AlexaSkillsKit.Interfaces.AudioPlayer}/IAudioPlayerSpeechlet.cs (75%) rename {AlexaSkillsKit.Lib/Interfaces/AudioPlayer => AlexaSkillsKit.Interfaces.AudioPlayer}/IAudioPlayerSpeechletAsync.cs (71%) rename {AlexaSkillsKit.Lib/Interfaces/AudioPlayer => AlexaSkillsKit.Interfaces.AudioPlayer}/PlaybackControllerRequest.cs (75%) create mode 100644 AlexaSkillsKit.Interfaces.Dialog/AlexaSkillsKit.Interfaces.Dialog.csproj rename {AlexaSkillsKit.Lib/Interfaces/Dialog => AlexaSkillsKit.Interfaces.Dialog}/Directives/DialogConfirmIntentDirective.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/Dialog => AlexaSkillsKit.Interfaces.Dialog}/Directives/DialogConfirmSlotDirective.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/Dialog => AlexaSkillsKit.Interfaces.Dialog}/Directives/DialogDelegateDirective.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/Dialog => AlexaSkillsKit.Interfaces.Dialog}/Directives/DialogDirective.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/Dialog => AlexaSkillsKit.Interfaces.Dialog}/Directives/DialogElicitSlotDirective.cs (100%) create mode 100644 AlexaSkillsKit.Interfaces.Display/AlexaSkillsKit.Interfaces.Display.csproj rename {AlexaSkillsKit.Lib/Interfaces/Display => AlexaSkillsKit.Interfaces.Display}/Directives/DisplayRenderTemplateDirective.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/Display => AlexaSkillsKit.Interfaces.Display}/Directives/HintDirective.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/Display => AlexaSkillsKit.Interfaces.Display}/DisplayImage.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/Display => AlexaSkillsKit.Interfaces.Display}/DisplayImageSource.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/Display => AlexaSkillsKit.Interfaces.Display}/DisplayInterface.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/Display => AlexaSkillsKit.Interfaces.Display}/DisplayRequest.cs (80%) rename {AlexaSkillsKit.Lib/Interfaces/Display => AlexaSkillsKit.Interfaces.Display}/DisplaySpeechletAsyncWrapper.cs (80%) create mode 100644 AlexaSkillsKit.Interfaces.Display/DisplaySpeechletServiceExtensions.cs rename {AlexaSkillsKit.Lib/Interfaces/Display => AlexaSkillsKit.Interfaces.Display}/DisplayTemplate.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/Display => AlexaSkillsKit.Interfaces.Display}/IDisplaySpeechlet.cs (63%) rename {AlexaSkillsKit.Lib/Interfaces/Display => AlexaSkillsKit.Interfaces.Display}/IDisplaySpeechletAsync.cs (58%) rename {AlexaSkillsKit.Lib/Interfaces/Display => AlexaSkillsKit.Interfaces.Display}/ListItem.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/Display => AlexaSkillsKit.Interfaces.Display}/TextContent.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/Display => AlexaSkillsKit.Interfaces.Display}/TextField.cs (100%) create mode 100644 AlexaSkillsKit.Interfaces.VideoApp/AlexaSkillsKit.Interfaces.VideoApp.csproj rename {AlexaSkillsKit.Lib/Interfaces/VideoApp => AlexaSkillsKit.Interfaces.VideoApp}/Directives/VideoAppLaunchDirective.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/VideoApp => AlexaSkillsKit.Interfaces.VideoApp}/VideoItem.cs (100%) rename {AlexaSkillsKit.Lib/Interfaces/VideoApp => AlexaSkillsKit.Interfaces.VideoApp}/VideoItemMetadata.cs (100%) create mode 100644 AlexaSkillsKit.Lib/AlexaSkillsKit.Core.csproj delete mode 100644 AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj delete mode 100644 AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerSpeechletExtensions.cs delete mode 100644 AlexaSkillsKit.Lib/Interfaces/Display/DisplaySpeechletExtensions.cs delete mode 100644 AlexaSkillsKit.Lib/Properties/AssemblyInfo.cs rename AlexaSkillsKit.Lib/{Speechlet => }/Requests/ExtendedSpeechletRequest.cs (95%) rename AlexaSkillsKit.Lib/{Speechlet => }/Requests/IntentRequest.cs (96%) rename AlexaSkillsKit.Lib/{Speechlet => }/Requests/LaunchRequest.cs (87%) rename AlexaSkillsKit.Lib/{Speechlet => }/Requests/SessionEndedRequest.cs (95%) rename AlexaSkillsKit.Lib/{Speechlet => }/Requests/SessionStartedRequest.cs (87%) rename AlexaSkillsKit.Lib/{Speechlet => }/Requests/SpeechletRequest.cs (96%) rename AlexaSkillsKit.Lib/{Speechlet => }/Requests/SystemExceptionEncounteredRequest.cs (87%) rename AlexaSkillsKit.Lib/{ => Speechlet}/AsyncHelpers.cs (100%) rename AlexaSkillsKit.Lib/{Interfaces/AudioPlayer => Speechlet}/AudioPlayerState.cs (100%) create mode 100644 AlexaSkillsKit.Lib/Speechlet/ISystemSpeechlet.cs create mode 100644 AlexaSkillsKit.Lib/Speechlet/ISystemSpeechletAsync.cs create mode 100644 AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs delete mode 100644 AlexaSkillsKit.Lib/Speechlet/SpeechletExtensions.cs delete mode 100644 AlexaSkillsKit.Lib/Speechlet/SpeechletRequestHandler.cs create mode 100644 AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs create mode 100644 AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs create mode 100644 AlexaSkillsKit.Lib/Speechlet/SpeechletValidationException.cs create mode 100644 AlexaSkillsKit.Lib/Speechlet/SystemSpeechletAsyncWrapper.cs delete mode 100644 AlexaSkillsKit.Lib/app.config delete mode 100644 AlexaSkillsKit.Lib/packages.config diff --git a/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj b/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj new file mode 100644 index 0000000..a4b601f --- /dev/null +++ b/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj @@ -0,0 +1,29 @@ + + + + netstandard1.1 + true + Stefan Negritoiu + Stefan Negritoiu + AlexaSkillsKit + 1.5.0 + Copyright © 2015 Stefan Negritoiu (FreeBusy) + MIT + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git + git + Amazon Alexa Skill System.Net.Http + + + + AnyCPU + + + + + + + + + + + diff --git a/AlexaSkillsKit.Http/SpeechletServiceExtensions.cs b/AlexaSkillsKit.Http/SpeechletServiceExtensions.cs new file mode 100644 index 0000000..85270ca --- /dev/null +++ b/AlexaSkillsKit.Http/SpeechletServiceExtensions.cs @@ -0,0 +1,63 @@ +// Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. + +using AlexaSkillsKit.Speechlet; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace AlexaSkillsKit.Http +{ + public static class SpeechletServiceExtensions + { + /// + /// Processes Alexa request AND validates request signature + /// + /// + /// + public static async Task GetResponseAsync(this SpeechletService service, HttpRequestMessage httpRequest) { + string chainUrl = null; + if (!httpRequest.Headers.Contains(Sdk.SIGNATURE_CERT_URL_REQUEST_HEADER)) { + chainUrl = httpRequest.Headers.GetValues(Sdk.SIGNATURE_CERT_URL_REQUEST_HEADER).FirstOrDefault(x => !string.IsNullOrEmpty(x)); + } + + string signature = null; + if (!httpRequest.Headers.Contains(Sdk.SIGNATURE_REQUEST_HEADER)) { + signature = httpRequest.Headers.GetValues(Sdk.SIGNATURE_REQUEST_HEADER).FirstOrDefault(x => !string.IsNullOrEmpty(x)); + } + + var content = await httpRequest.Content.ReadAsStringAsync(); + + try { + var alexaRequest = await service.GetRequestAsync(content, chainUrl, signature); + var alexaResponse = await service.ProcessRequestAsync(alexaRequest); + var json = alexaResponse?.ToJson(); + + return (json == null) ? + new HttpResponseMessage(HttpStatusCode.InternalServerError) : + new HttpResponseMessage(HttpStatusCode.OK) { + Content = new StringContent(json, Encoding.UTF8, "application/json") + }; + } + catch (SpeechletValidationException ex) { + return new HttpResponseMessage(HttpStatusCode.BadRequest) { + ReasonPhrase = ex.ValidationResult.ToString() + }; + } + } + + /// + /// Processes Alexa request AND validates request signature + /// + /// + /// + public static HttpResponseMessage GetResponse(this SpeechletBase speechlet, HttpRequestMessage httpRequest) { + return AsyncHelpers.RunSync(async () => await speechlet.Service.GetResponseAsync(httpRequest)); + } + + public static async Task GetResponseAsync(this SpeechletBase speechlet, HttpRequestMessage httpRequest) { + return await speechlet.Service.GetResponseAsync(httpRequest); + } + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Interfaces.AudioPlayer/AlexaSkillsKit.Interfaces.AudioPlayer.csproj b/AlexaSkillsKit.Interfaces.AudioPlayer/AlexaSkillsKit.Interfaces.AudioPlayer.csproj new file mode 100644 index 0000000..0a44c7d --- /dev/null +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/AlexaSkillsKit.Interfaces.AudioPlayer.csproj @@ -0,0 +1,21 @@ + + + + netstandard1.1 + AlexaSkillsKit + Copyright © 2015 Stefan Negritoiu (FreeBusy) + + Stefan Negritoiu + 1.5.0 + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git + git + MIT + Amazon Alexa Skill .Net + true + + + + + + + diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioItem.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioItem.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioItem.cs rename to AlexaSkillsKit.Interfaces.AudioPlayer/AudioItem.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioItemStream.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioItemStream.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioItemStream.cs rename to AlexaSkillsKit.Interfaces.AudioPlayer/AudioItemStream.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerInterface.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerInterface.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerInterface.cs rename to AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerInterface.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerPlaybackFailedRequest.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerPlaybackFailedRequest.cs similarity index 86% rename from AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerPlaybackFailedRequest.cs rename to AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerPlaybackFailedRequest.cs index 9685e09..d8f65dd 100644 --- a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerPlaybackFailedRequest.cs +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerPlaybackFailedRequest.cs @@ -1,6 +1,7 @@ -using Newtonsoft.Json.Linq; +using AlexaSkillsKit.Speechlet; +using Newtonsoft.Json.Linq; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Interfaces.AudioPlayer { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#playbackfailed diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerRequest.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerRequest.cs similarity index 84% rename from AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerRequest.cs rename to AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerRequest.cs index 2b24f6a..8f7218a 100644 --- a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerRequest.cs +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerRequest.cs @@ -1,6 +1,7 @@ -using Newtonsoft.Json.Linq; +using AlexaSkillsKit.Requests; +using Newtonsoft.Json.Linq; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Interfaces.AudioPlayer { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#requests diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerResponse.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerResponse.cs similarity index 75% rename from AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerResponse.cs rename to AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerResponse.cs index 0b3e4a7..a849336 100644 --- a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerResponse.cs +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerResponse.cs @@ -1,8 +1,8 @@ -// Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. - +using AlexaSkillsKit.Interfaces.AudioPlayer.Directives; +using AlexaSkillsKit.Speechlet; using System.Collections.Generic; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Interfaces.AudioPlayer { /// /// https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#response-to-audioplayer-or-playbackcontroller-example-directives-only diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerSpeechletAsyncWrapper.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerSpeechletAsyncWrapper.cs similarity index 86% rename from AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerSpeechletAsyncWrapper.cs rename to AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerSpeechletAsyncWrapper.cs index 9d95802..77ec572 100644 --- a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerSpeechletAsyncWrapper.cs +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerSpeechletAsyncWrapper.cs @@ -1,6 +1,7 @@ -using System.Threading.Tasks; +using AlexaSkillsKit.Speechlet; +using System.Threading.Tasks; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Interfaces.AudioPlayer { public class AudioPlayerSpeechletAsyncWrapper : IAudioPlayerSpeechletAsync { diff --git a/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerSpeechletServiceExtensions.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerSpeechletServiceExtensions.cs new file mode 100644 index 0000000..e650b34 --- /dev/null +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerSpeechletServiceExtensions.cs @@ -0,0 +1,31 @@ +using AlexaSkillsKit.Json; +using AlexaSkillsKit.Speechlet; + +namespace AlexaSkillsKit.Interfaces.AudioPlayer +{ + public static class AudioPlayerSpeechletServiceExtensions + { + public static void AddAudioPlayer(this SpeechletService service, IAudioPlayerSpeechlet speechlet) { + service.AddAudioPlayer(new AudioPlayerSpeechletAsyncWrapper(speechlet)); + } + + public static void AddAudioPlayer(this SpeechletService service, IAudioPlayerSpeechletAsync speechlet) { + Deserializer.RegisterDeserializer("AudioPlayer", AudioPlayerInterface.FromJson); + + var audioPlayerResolver = new InterfaceResolver() + .WithDefaultDeserializer((json, subtype) => new AudioPlayerRequest(json, subtype)) + .WithDeserializer("PlaybackFailed", (json, subtype) => new AudioPlayerPlaybackFailedRequest(json, subtype)); + SpeechletRequestResolver.AddInterface("AudioPlayer", audioPlayerResolver); + + service.AddHandler( + async (request, context) => await speechlet.OnAudioPlayerAsync(request, context)); + + var playbackControllerResolver = new InterfaceResolver() + .WithDefaultDeserializer((json, subtype) => new PlaybackControllerRequest(json, subtype)); + SpeechletRequestResolver.AddInterface("PlaybackController", playbackControllerResolver); + + service.AddHandler( + async (request, context) => await speechlet.OnPlaybackControllerAsync(request, context)); + } + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerClearQueueDirective.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/Directives/AudioPlayerClearQueueDirective.cs similarity index 84% rename from AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerClearQueueDirective.cs rename to AlexaSkillsKit.Interfaces.AudioPlayer/Directives/AudioPlayerClearQueueDirective.cs index 2878da0..b8da7c4 100644 --- a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerClearQueueDirective.cs +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/Directives/AudioPlayerClearQueueDirective.cs @@ -1,6 +1,4 @@ -using AlexaSkillsKit.Speechlet; - -namespace AlexaSkillsKit.Interfaces.AudioPlayer.Directives +namespace AlexaSkillsKit.Interfaces.AudioPlayer.Directives { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#clearqueue diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerDirective.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/Directives/AudioPlayerDirective.cs similarity index 76% rename from AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerDirective.cs rename to AlexaSkillsKit.Interfaces.AudioPlayer/Directives/AudioPlayerDirective.cs index a8ef099..42a6689 100644 --- a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerDirective.cs +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/Directives/AudioPlayerDirective.cs @@ -1,4 +1,6 @@ -namespace AlexaSkillsKit.Speechlet +using AlexaSkillsKit.Speechlet; + +namespace AlexaSkillsKit.Interfaces.AudioPlayer.Directives { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#directives diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerPlayDirective.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/Directives/AudioPlayerPlayDirective.cs similarity index 86% rename from AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerPlayDirective.cs rename to AlexaSkillsKit.Interfaces.AudioPlayer/Directives/AudioPlayerPlayDirective.cs index 175adc3..05fd6f7 100644 --- a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerPlayDirective.cs +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/Directives/AudioPlayerPlayDirective.cs @@ -1,6 +1,4 @@ -using AlexaSkillsKit.Speechlet; - -namespace AlexaSkillsKit.Interfaces.AudioPlayer.Directives +namespace AlexaSkillsKit.Interfaces.AudioPlayer.Directives { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#play diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerStopDirective.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/Directives/AudioPlayerStopDirective.cs similarity index 75% rename from AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerStopDirective.cs rename to AlexaSkillsKit.Interfaces.AudioPlayer/Directives/AudioPlayerStopDirective.cs index 7a418f8..52bb635 100644 --- a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/Directives/AudioPlayerStopDirective.cs +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/Directives/AudioPlayerStopDirective.cs @@ -1,6 +1,4 @@ -using AlexaSkillsKit.Speechlet; - -namespace AlexaSkillsKit.Interfaces.AudioPlayer.Directives +namespace AlexaSkillsKit.Interfaces.AudioPlayer.Directives { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#stop diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/IAudioPlayerSpeechlet.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/IAudioPlayerSpeechlet.cs similarity index 75% rename from AlexaSkillsKit.Lib/Interfaces/AudioPlayer/IAudioPlayerSpeechlet.cs rename to AlexaSkillsKit.Interfaces.AudioPlayer/IAudioPlayerSpeechlet.cs index dbce928..60ce0b2 100644 --- a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/IAudioPlayerSpeechlet.cs +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/IAudioPlayerSpeechlet.cs @@ -1,4 +1,6 @@ -namespace AlexaSkillsKit.Speechlet +using AlexaSkillsKit.Speechlet; + +namespace AlexaSkillsKit.Interfaces.AudioPlayer { public interface IAudioPlayerSpeechlet { diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/IAudioPlayerSpeechletAsync.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/IAudioPlayerSpeechletAsync.cs similarity index 71% rename from AlexaSkillsKit.Lib/Interfaces/AudioPlayer/IAudioPlayerSpeechletAsync.cs rename to AlexaSkillsKit.Interfaces.AudioPlayer/IAudioPlayerSpeechletAsync.cs index fa0ad60..774d5b5 100644 --- a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/IAudioPlayerSpeechletAsync.cs +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/IAudioPlayerSpeechletAsync.cs @@ -1,6 +1,7 @@ -using System.Threading.Tasks; +using AlexaSkillsKit.Speechlet; +using System.Threading.Tasks; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Interfaces.AudioPlayer { public interface IAudioPlayerSpeechletAsync { diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/PlaybackControllerRequest.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/PlaybackControllerRequest.cs similarity index 75% rename from AlexaSkillsKit.Lib/Interfaces/AudioPlayer/PlaybackControllerRequest.cs rename to AlexaSkillsKit.Interfaces.AudioPlayer/PlaybackControllerRequest.cs index 453dc75..7e1e40f 100644 --- a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/PlaybackControllerRequest.cs +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/PlaybackControllerRequest.cs @@ -1,6 +1,7 @@ -using Newtonsoft.Json.Linq; +using AlexaSkillsKit.Requests; +using Newtonsoft.Json.Linq; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Interfaces.AudioPlayer { /// /// https://developer.amazon.com/docs/custom-skills/playback-controller-interface-reference.html#requests diff --git a/AlexaSkillsKit.Interfaces.Dialog/AlexaSkillsKit.Interfaces.Dialog.csproj b/AlexaSkillsKit.Interfaces.Dialog/AlexaSkillsKit.Interfaces.Dialog.csproj new file mode 100644 index 0000000..0a44c7d --- /dev/null +++ b/AlexaSkillsKit.Interfaces.Dialog/AlexaSkillsKit.Interfaces.Dialog.csproj @@ -0,0 +1,21 @@ + + + + netstandard1.1 + AlexaSkillsKit + Copyright © 2015 Stefan Negritoiu (FreeBusy) + + Stefan Negritoiu + 1.5.0 + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git + git + MIT + Amazon Alexa Skill .Net + true + + + + + + + diff --git a/AlexaSkillsKit.Lib/Interfaces/Dialog/Directives/DialogConfirmIntentDirective.cs b/AlexaSkillsKit.Interfaces.Dialog/Directives/DialogConfirmIntentDirective.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/Dialog/Directives/DialogConfirmIntentDirective.cs rename to AlexaSkillsKit.Interfaces.Dialog/Directives/DialogConfirmIntentDirective.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/Dialog/Directives/DialogConfirmSlotDirective.cs b/AlexaSkillsKit.Interfaces.Dialog/Directives/DialogConfirmSlotDirective.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/Dialog/Directives/DialogConfirmSlotDirective.cs rename to AlexaSkillsKit.Interfaces.Dialog/Directives/DialogConfirmSlotDirective.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/Dialog/Directives/DialogDelegateDirective.cs b/AlexaSkillsKit.Interfaces.Dialog/Directives/DialogDelegateDirective.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/Dialog/Directives/DialogDelegateDirective.cs rename to AlexaSkillsKit.Interfaces.Dialog/Directives/DialogDelegateDirective.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/Dialog/Directives/DialogDirective.cs b/AlexaSkillsKit.Interfaces.Dialog/Directives/DialogDirective.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/Dialog/Directives/DialogDirective.cs rename to AlexaSkillsKit.Interfaces.Dialog/Directives/DialogDirective.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/Dialog/Directives/DialogElicitSlotDirective.cs b/AlexaSkillsKit.Interfaces.Dialog/Directives/DialogElicitSlotDirective.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/Dialog/Directives/DialogElicitSlotDirective.cs rename to AlexaSkillsKit.Interfaces.Dialog/Directives/DialogElicitSlotDirective.cs diff --git a/AlexaSkillsKit.Interfaces.Display/AlexaSkillsKit.Interfaces.Display.csproj b/AlexaSkillsKit.Interfaces.Display/AlexaSkillsKit.Interfaces.Display.csproj new file mode 100644 index 0000000..0a44c7d --- /dev/null +++ b/AlexaSkillsKit.Interfaces.Display/AlexaSkillsKit.Interfaces.Display.csproj @@ -0,0 +1,21 @@ + + + + netstandard1.1 + AlexaSkillsKit + Copyright © 2015 Stefan Negritoiu (FreeBusy) + + Stefan Negritoiu + 1.5.0 + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git + git + MIT + Amazon Alexa Skill .Net + true + + + + + + + diff --git a/AlexaSkillsKit.Lib/Interfaces/Display/Directives/DisplayRenderTemplateDirective.cs b/AlexaSkillsKit.Interfaces.Display/Directives/DisplayRenderTemplateDirective.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/Display/Directives/DisplayRenderTemplateDirective.cs rename to AlexaSkillsKit.Interfaces.Display/Directives/DisplayRenderTemplateDirective.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/Display/Directives/HintDirective.cs b/AlexaSkillsKit.Interfaces.Display/Directives/HintDirective.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/Display/Directives/HintDirective.cs rename to AlexaSkillsKit.Interfaces.Display/Directives/HintDirective.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/Display/DisplayImage.cs b/AlexaSkillsKit.Interfaces.Display/DisplayImage.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/Display/DisplayImage.cs rename to AlexaSkillsKit.Interfaces.Display/DisplayImage.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/Display/DisplayImageSource.cs b/AlexaSkillsKit.Interfaces.Display/DisplayImageSource.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/Display/DisplayImageSource.cs rename to AlexaSkillsKit.Interfaces.Display/DisplayImageSource.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/Display/DisplayInterface.cs b/AlexaSkillsKit.Interfaces.Display/DisplayInterface.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/Display/DisplayInterface.cs rename to AlexaSkillsKit.Interfaces.Display/DisplayInterface.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/Display/DisplayRequest.cs b/AlexaSkillsKit.Interfaces.Display/DisplayRequest.cs similarity index 80% rename from AlexaSkillsKit.Lib/Interfaces/Display/DisplayRequest.cs rename to AlexaSkillsKit.Interfaces.Display/DisplayRequest.cs index 06464a0..bb4cf3c 100644 --- a/AlexaSkillsKit.Lib/Interfaces/Display/DisplayRequest.cs +++ b/AlexaSkillsKit.Interfaces.Display/DisplayRequest.cs @@ -1,6 +1,7 @@ -using Newtonsoft.Json.Linq; +using AlexaSkillsKit.Requests; +using Newtonsoft.Json.Linq; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Interfaces.Display { /// /// https://developer.amazon.com/docs/custom-skills/display-interface-reference.html#touch-selection-events diff --git a/AlexaSkillsKit.Lib/Interfaces/Display/DisplaySpeechletAsyncWrapper.cs b/AlexaSkillsKit.Interfaces.Display/DisplaySpeechletAsyncWrapper.cs similarity index 80% rename from AlexaSkillsKit.Lib/Interfaces/Display/DisplaySpeechletAsyncWrapper.cs rename to AlexaSkillsKit.Interfaces.Display/DisplaySpeechletAsyncWrapper.cs index 0fd4a8f..50b0c7a 100644 --- a/AlexaSkillsKit.Lib/Interfaces/Display/DisplaySpeechletAsyncWrapper.cs +++ b/AlexaSkillsKit.Interfaces.Display/DisplaySpeechletAsyncWrapper.cs @@ -1,6 +1,7 @@ -using System.Threading.Tasks; +using AlexaSkillsKit.Speechlet; +using System.Threading.Tasks; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Interfaces.Display { public class DisplaySpeechletAsyncWrapper : IDisplaySpeechletAsync { diff --git a/AlexaSkillsKit.Interfaces.Display/DisplaySpeechletServiceExtensions.cs b/AlexaSkillsKit.Interfaces.Display/DisplaySpeechletServiceExtensions.cs new file mode 100644 index 0000000..b08296b --- /dev/null +++ b/AlexaSkillsKit.Interfaces.Display/DisplaySpeechletServiceExtensions.cs @@ -0,0 +1,22 @@ +using AlexaSkillsKit.Json; +using AlexaSkillsKit.Speechlet; + +namespace AlexaSkillsKit.Interfaces.Display +{ + public static class DisplaySpeechletServiceExtensions + { + public static void AddDisplay(this SpeechletService service, IDisplaySpeechlet speechlet) { + service.AddDisplay(new DisplaySpeechletAsyncWrapper(speechlet)); + } + + public static void AddDisplay(this SpeechletService service, IDisplaySpeechletAsync speechlet) { + Deserializer.RegisterDeserializer("Display", DisplayInterface.FromJson); + + var displayResolver = new InterfaceResolver() + .WithDefaultDeserializer((json, subtype) => new DisplayRequest(json, subtype)); + SpeechletRequestResolver.AddInterface("Display", displayResolver); + + service.AddHandler(async (request, context) => await speechlet.OnDisplayAsync(request, context)); + } + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Interfaces/Display/DisplayTemplate.cs b/AlexaSkillsKit.Interfaces.Display/DisplayTemplate.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/Display/DisplayTemplate.cs rename to AlexaSkillsKit.Interfaces.Display/DisplayTemplate.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/Display/IDisplaySpeechlet.cs b/AlexaSkillsKit.Interfaces.Display/IDisplaySpeechlet.cs similarity index 63% rename from AlexaSkillsKit.Lib/Interfaces/Display/IDisplaySpeechlet.cs rename to AlexaSkillsKit.Interfaces.Display/IDisplaySpeechlet.cs index 30b07fe..13ce303 100644 --- a/AlexaSkillsKit.Lib/Interfaces/Display/IDisplaySpeechlet.cs +++ b/AlexaSkillsKit.Interfaces.Display/IDisplaySpeechlet.cs @@ -1,4 +1,6 @@ -namespace AlexaSkillsKit.Speechlet +using AlexaSkillsKit.Speechlet; + +namespace AlexaSkillsKit.Interfaces.Display { public interface IDisplaySpeechlet { diff --git a/AlexaSkillsKit.Lib/Interfaces/Display/IDisplaySpeechletAsync.cs b/AlexaSkillsKit.Interfaces.Display/IDisplaySpeechletAsync.cs similarity index 58% rename from AlexaSkillsKit.Lib/Interfaces/Display/IDisplaySpeechletAsync.cs rename to AlexaSkillsKit.Interfaces.Display/IDisplaySpeechletAsync.cs index efe73a9..2e61cd5 100644 --- a/AlexaSkillsKit.Lib/Interfaces/Display/IDisplaySpeechletAsync.cs +++ b/AlexaSkillsKit.Interfaces.Display/IDisplaySpeechletAsync.cs @@ -1,6 +1,7 @@ -using System.Threading.Tasks; +using AlexaSkillsKit.Speechlet; +using System.Threading.Tasks; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Interfaces.Display { public interface IDisplaySpeechletAsync { diff --git a/AlexaSkillsKit.Lib/Interfaces/Display/ListItem.cs b/AlexaSkillsKit.Interfaces.Display/ListItem.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/Display/ListItem.cs rename to AlexaSkillsKit.Interfaces.Display/ListItem.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/Display/TextContent.cs b/AlexaSkillsKit.Interfaces.Display/TextContent.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/Display/TextContent.cs rename to AlexaSkillsKit.Interfaces.Display/TextContent.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/Display/TextField.cs b/AlexaSkillsKit.Interfaces.Display/TextField.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/Display/TextField.cs rename to AlexaSkillsKit.Interfaces.Display/TextField.cs diff --git a/AlexaSkillsKit.Interfaces.VideoApp/AlexaSkillsKit.Interfaces.VideoApp.csproj b/AlexaSkillsKit.Interfaces.VideoApp/AlexaSkillsKit.Interfaces.VideoApp.csproj new file mode 100644 index 0000000..0a44c7d --- /dev/null +++ b/AlexaSkillsKit.Interfaces.VideoApp/AlexaSkillsKit.Interfaces.VideoApp.csproj @@ -0,0 +1,21 @@ + + + + netstandard1.1 + AlexaSkillsKit + Copyright © 2015 Stefan Negritoiu (FreeBusy) + + Stefan Negritoiu + 1.5.0 + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git + git + MIT + Amazon Alexa Skill .Net + true + + + + + + + diff --git a/AlexaSkillsKit.Lib/Interfaces/VideoApp/Directives/VideoAppLaunchDirective.cs b/AlexaSkillsKit.Interfaces.VideoApp/Directives/VideoAppLaunchDirective.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/VideoApp/Directives/VideoAppLaunchDirective.cs rename to AlexaSkillsKit.Interfaces.VideoApp/Directives/VideoAppLaunchDirective.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/VideoApp/VideoItem.cs b/AlexaSkillsKit.Interfaces.VideoApp/VideoItem.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/VideoApp/VideoItem.cs rename to AlexaSkillsKit.Interfaces.VideoApp/VideoItem.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/VideoApp/VideoItemMetadata.cs b/AlexaSkillsKit.Interfaces.VideoApp/VideoItemMetadata.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/VideoApp/VideoItemMetadata.cs rename to AlexaSkillsKit.Interfaces.VideoApp/VideoItemMetadata.cs diff --git a/AlexaSkillsKit.Lib/AlexaSkillsKit.Core.csproj b/AlexaSkillsKit.Lib/AlexaSkillsKit.Core.csproj new file mode 100644 index 0000000..ca1c7c5 --- /dev/null +++ b/AlexaSkillsKit.Lib/AlexaSkillsKit.Core.csproj @@ -0,0 +1,26 @@ + + + + netstandard1.1 + AlexaSkillsKit + Copyright © 2015 Stefan Negritoiu (FreeBusy) + + Stefan Negritoiu + 1.5.0 + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git + git + MIT + Amazon Alexa Skill .Net + true + + + + + + + + + + + + diff --git a/AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj b/AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj deleted file mode 100644 index b79cde5..0000000 --- a/AlexaSkillsKit.Lib/AlexaSkillsKit.Lib.csproj +++ /dev/null @@ -1,166 +0,0 @@ - - - - - Debug - AnyCPU - {0EC882A8-AACA-4BD5-B449-72F20FDB8586} - Library - Properties - AlexaSkillsKit - AlexaSkillsKit - v4.5 - 512 - ..\ - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll - True - - - ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll - True - - - - - - - ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Authentication/SpeechletRequestSignatureVerifier.cs b/AlexaSkillsKit.Lib/Authentication/SpeechletRequestSignatureVerifier.cs index d5dbc6e..9f25230 100644 --- a/AlexaSkillsKit.Lib/Authentication/SpeechletRequestSignatureVerifier.cs +++ b/AlexaSkillsKit.Lib/Authentication/SpeechletRequestSignatureVerifier.cs @@ -1,30 +1,23 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.X509; using System; using System.Collections; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Net; using System.Net.Http; -using System.Runtime.Caching; -using System.Text; -using System.Text.RegularExpressions; using System.Threading.Tasks; -using Org.BouncyCastle.X509; -using Org.BouncyCastle.Security.Certificates; namespace AlexaSkillsKit.Authentication { public class SpeechletRequestSignatureVerifier { - private static Func _getCertCacheKey = (string url) => string.Format("{0}_{1}", Sdk.SIGNATURE_CERT_URL_REQUEST_HEADER, url); - - private static CacheItemPolicy _policy = new CacheItemPolicy { - Priority = CacheItemPriority.Default, - AbsoluteExpiration = DateTimeOffset.UtcNow.AddHours(24) - }; + private const string HttpsUriScheme = "https"; + private static Func _getCertCacheKey = (string url) => string.Format("{0}_{1}", Sdk.SIGNATURE_CERT_URL_REQUEST_HEADER, url); + private static Dictionary _certCache = new Dictionary(); + private static DateTimeOffset _certCacheAbsoluteExpiration = DateTimeOffset.UtcNow.AddHours(24); /// /// Verifying the Signature Certificate URL per requirements documented at @@ -43,36 +36,10 @@ public static bool VerifyCertificateUrl(string certChainUrl) { return certChainUri.Host.Equals(Sdk.SIGNATURE_CERT_URL_HOST, StringComparison.OrdinalIgnoreCase) && certChainUri.PathAndQuery.StartsWith(Sdk.SIGNATURE_CERT_URL_PATH) && - certChainUri.Scheme == Uri.UriSchemeHttps && + certChainUri.Scheme.ToLowerInvariant() == HttpsUriScheme && certChainUri.Port == 443; } - - /// - /// Verifies request signature and manages the caching of the signature certificate - /// - public static bool VerifyRequestSignature( - byte[] serializedSpeechletRequest, string expectedSignature, string certChainUrl) { - - string certCacheKey = _getCertCacheKey(certChainUrl); - X509Certificate cert = MemoryCache.Default.Get(certCacheKey) as X509Certificate; - if (cert == null || - !CheckRequestSignature(serializedSpeechletRequest, expectedSignature, cert)) { - - // download the cert - // if we don't have it in cache or - // if we have it but it's stale because the current request was signed with a newer cert - // (signaled by signature check fail with cached cert) - cert = RetrieveAndVerifyCertificate(certChainUrl); - if (cert == null) return false; - - MemoryCache.Default.Set(certCacheKey, cert, _policy); - } - - return CheckRequestSignature(serializedSpeechletRequest, expectedSignature, cert); - } - - /// /// Verifies request signature and manages the caching of the signature certificate /// @@ -80,7 +47,7 @@ public async static Task VerifyRequestSignatureAsync( byte[] serializedSpeechletRequest, string expectedSignature, string certChainUrl) { string certCacheKey = _getCertCacheKey(certChainUrl); - X509Certificate cert = MemoryCache.Default.Get(certCacheKey) as X509Certificate; + X509Certificate cert = GetCachedCertificate(certCacheKey) as X509Certificate; if (cert == null || !CheckRequestSignature(serializedSpeechletRequest, expectedSignature, cert)) { @@ -91,41 +58,12 @@ public async static Task VerifyRequestSignatureAsync( cert = await RetrieveAndVerifyCertificateAsync(certChainUrl); if (cert == null) return false; - MemoryCache.Default.Set(certCacheKey, cert, _policy); + SetCachedCertificate(certCacheKey, cert); } return CheckRequestSignature(serializedSpeechletRequest, expectedSignature, cert); } - - /// - /// - /// - public static X509Certificate RetrieveAndVerifyCertificate(string certChainUrl) { - // making requests to externally-supplied URLs is an open invitation to DoS - // so restrict host to an Alexa controlled subdomain/path - if (!VerifyCertificateUrl(certChainUrl)) return null; - - var webClient = new WebClient(); - var content = webClient.DownloadString(certChainUrl); - - var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(new StringReader(content)); - var cert = (X509Certificate)pemReader.ReadObject(); - try { - cert.CheckValidity(); - if (!CheckCertSubjectNames(cert)) return null; - } - catch (CertificateExpiredException) { - return null; - } - catch (CertificateNotYetValidException) { - return null; - } - - return cert; - } - - /// /// /// @@ -184,9 +122,9 @@ public static bool CheckRequestSignature( /// private static bool CheckCertSubjectNames(X509Certificate cert) { bool found = false; - ArrayList subjectNamesList = (ArrayList)cert.GetSubjectAlternativeNames(); + var subjectNamesList = (IList)cert.GetSubjectAlternativeNames(); for (int i=0; i < subjectNamesList.Count; i++) { - ArrayList subjectNames = (ArrayList)subjectNamesList[i]; + var subjectNames = (IList)subjectNamesList[i]; for (int j = 0; j < subjectNames.Count; j++) { if (subjectNames[j] is String && subjectNames[j].Equals(Sdk.ECHO_API_DOMAIN_NAME)) { found = true; @@ -197,5 +135,25 @@ private static bool CheckCertSubjectNames(X509Certificate cert) { return found; } + + /// + /// + /// + private static X509Certificate GetCachedCertificate(string url) { + var key = _getCertCacheKey(url); + if (_certCacheAbsoluteExpiration < DateTimeOffset.UtcNow) { + _certCache[key] = null; + } + + return _certCache[key]; + } + + /// + /// + /// + private static void SetCachedCertificate(string url, X509Certificate certificate) { + var key = _getCertCacheKey(url); + _certCache[key] = certificate; + } } } diff --git a/AlexaSkillsKit.Lib/Authentication/SpeechletRequestValidationResult.cs b/AlexaSkillsKit.Lib/Authentication/SpeechletRequestValidationResult.cs index 42f5c16..ddea5bc 100644 --- a/AlexaSkillsKit.Lib/Authentication/SpeechletRequestValidationResult.cs +++ b/AlexaSkillsKit.Lib/Authentication/SpeechletRequestValidationResult.cs @@ -12,6 +12,9 @@ public enum SpeechletRequestValidationResult NoCertHeader = 2, InvalidSignature = 4, InvalidTimestamp = 8, - InvalidJson = 16 + InvalidJson = 16, + InvalidApplicationId = 32, + NoContent = 64, + InvalidVersion = 128, } } \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Helpers/DateTimeHelpers.cs b/AlexaSkillsKit.Lib/Helpers/DateTimeHelpers.cs index e01f4cc..d4f7d60 100644 --- a/AlexaSkillsKit.Lib/Helpers/DateTimeHelpers.cs +++ b/AlexaSkillsKit.Lib/Helpers/DateTimeHelpers.cs @@ -1,7 +1,6 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy) and contributors. See LICENSE file for more information. using System; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Helpers diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerSpeechletExtensions.cs b/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerSpeechletExtensions.cs deleted file mode 100644 index 69430b9..0000000 --- a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerSpeechletExtensions.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace AlexaSkillsKit.Speechlet -{ - public static class AudioPlayerSpeechletExtensions - { - public static void UseAudioPlayer(this IAudioPlayerSpeechlet speechlet) { - UseAudioPlayer(new AudioPlayerSpeechletAsyncWrapper(speechlet)); - } - - public static void UseAudioPlayer(this IAudioPlayerSpeechletAsync speechlet) { - var audioPlayerResolver = new InterfaceResolver() - .WithDefaultDeserializer((json, subtype) => new AudioPlayerRequest(json, subtype)) - .WithDeserializer("PlaybackFailed", (json, subtype) => new AudioPlayerPlaybackFailedRequest(json, subtype)); - SpeechletRequestResolver.UseInterface("AudioPlayer", audioPlayerResolver); - - SpeechletRequestHandler.UseInterface( - async (request, context) => await speechlet.OnAudioPlayerAsync(request, context)); - - var playbackControllerResolver = new InterfaceResolver() - .WithDefaultDeserializer((json, subtype) => new PlaybackControllerRequest(json, subtype)); - SpeechletRequestResolver.UseInterface("PlaybackController", playbackControllerResolver); - - SpeechletRequestHandler.UseInterface( - async (request, context) => await speechlet.OnPlaybackControllerAsync(request, context)); - } - } -} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Interfaces/Display/DisplaySpeechletExtensions.cs b/AlexaSkillsKit.Lib/Interfaces/Display/DisplaySpeechletExtensions.cs deleted file mode 100644 index 2a6133d..0000000 --- a/AlexaSkillsKit.Lib/Interfaces/Display/DisplaySpeechletExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace AlexaSkillsKit.Speechlet -{ - public static class DisplaySpeechletExtensions - { - public static void UseDisplay(this IDisplaySpeechlet speechlet) { - UseDisplay(new DisplaySpeechletAsyncWrapper(speechlet)); - } - - public static void UseDisplay(this IDisplaySpeechletAsync speechlet) { - var displayResolver = new InterfaceResolver() - .WithDefaultDeserializer((json, subtype) => new DisplayRequest(json, subtype)); - SpeechletRequestResolver.UseInterface("Display", displayResolver); - - SpeechletRequestHandler.UseInterface( - async (request, context) => await speechlet.OnDisplayAsync(request, context)); - } - } -} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Json/Deserializer.cs b/AlexaSkillsKit.Lib/Json/Deserializer.cs index 766ec4d..631ae17 100644 --- a/AlexaSkillsKit.Lib/Json/Deserializer.cs +++ b/AlexaSkillsKit.Lib/Json/Deserializer.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Json { public class Deserializer { diff --git a/AlexaSkillsKit.Lib/Json/InterfaceResolver.cs b/AlexaSkillsKit.Lib/Json/InterfaceResolver.cs index b61add3..9ef2f3a 100644 --- a/AlexaSkillsKit.Lib/Json/InterfaceResolver.cs +++ b/AlexaSkillsKit.Lib/Json/InterfaceResolver.cs @@ -1,8 +1,9 @@ -using Newtonsoft.Json.Linq; +using AlexaSkillsKit.Requests; +using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Json { public class InterfaceResolver { diff --git a/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs b/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs index 0c6c260..3f16b54 100644 --- a/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs +++ b/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs @@ -4,6 +4,8 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using AlexaSkillsKit.Speechlet; +using AlexaSkillsKit.Requests; +using AlexaSkillsKit.Authentication; namespace AlexaSkillsKit.Json { @@ -16,7 +18,7 @@ public class SpeechletRequestEnvelope /// public static SpeechletRequestEnvelope FromJson(string content) { if (String.IsNullOrEmpty(content)) { - throw new SpeechletException("Request content is empty"); + throw new SpeechletValidationException(SpeechletRequestValidationResult.NoContent, "Request content is empty"); } JObject json = JsonConvert.DeserializeObject(content, Sdk.DeserializationSettings); @@ -26,7 +28,7 @@ public static SpeechletRequestEnvelope FromJson(string content) { public static SpeechletRequestEnvelope FromJson(JObject json) { var version = json.Value("version"); if (version != null && version != Sdk.VERSION) { - throw new SpeechletException("Request must conform to 1.0 schema."); + throw new SpeechletValidationException(SpeechletRequestValidationResult.InvalidVersion, "Request must conform to 1.0 schema."); } return new SpeechletRequestEnvelope { diff --git a/AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs b/AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs index b27f283..682193f 100644 --- a/AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs +++ b/AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs @@ -1,7 +1,8 @@ -using Newtonsoft.Json.Linq; +using AlexaSkillsKit.Requests; +using Newtonsoft.Json.Linq; using System.Collections.Generic; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Json { public static class SpeechletRequestResolver { @@ -14,8 +15,22 @@ public static SpeechletRequest FromJson(string type, string subtype, JObject jso return resolvers[type].FromJson(subtype, json); } - public static void UseInterface(string name, InterfaceResolver resolver) { + public static void AddInterface(string name, InterfaceResolver resolver) { resolvers[name] = resolver; } + + public static void AddStandard() { + var standardResolver = new InterfaceResolver() + .WithDeserializer(nameof(LaunchRequest), (json, subtype) => new LaunchRequest(json)) + .WithDeserializer(nameof(IntentRequest), (json, subtype) => new IntentRequest(json)) + .WithDeserializer(nameof(SessionEndedRequest), (json, subtype) => new SessionEndedRequest(json)); + AddInterface(string.Empty, standardResolver); + } + + public static void AddSystem() { + var systemResolver = new InterfaceResolver() + .WithDeserializer("ExceptionEncountered", (json, subtype) => new SystemExceptionEncounteredRequest(json, subtype)); + AddInterface("System", systemResolver); + } } } \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Properties/AssemblyInfo.cs b/AlexaSkillsKit.Lib/Properties/AssemblyInfo.cs deleted file mode 100644 index d23afea..0000000 --- a/AlexaSkillsKit.Lib/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("AlexaSkillsKit")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("AlexaSkillsKit")] -[assembly: AssemblyCopyright("Copyright © 2015 Stefan Negritoiu (FreeBusy)")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("5471ba5b-32c1-486c-851b-dbb8d9c53f13")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.5.0")] -[assembly: AssemblyFileVersion("1.5.0")] diff --git a/AlexaSkillsKit.Lib/Speechlet/Requests/ExtendedSpeechletRequest.cs b/AlexaSkillsKit.Lib/Requests/ExtendedSpeechletRequest.cs similarity index 95% rename from AlexaSkillsKit.Lib/Speechlet/Requests/ExtendedSpeechletRequest.cs rename to AlexaSkillsKit.Lib/Requests/ExtendedSpeechletRequest.cs index 915e250..f2297d3 100644 --- a/AlexaSkillsKit.Lib/Speechlet/Requests/ExtendedSpeechletRequest.cs +++ b/AlexaSkillsKit.Lib/Requests/ExtendedSpeechletRequest.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json.Linq; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Requests { /// /// https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#request-body-parameters diff --git a/AlexaSkillsKit.Lib/Speechlet/Requests/IntentRequest.cs b/AlexaSkillsKit.Lib/Requests/IntentRequest.cs similarity index 96% rename from AlexaSkillsKit.Lib/Speechlet/Requests/IntentRequest.cs rename to AlexaSkillsKit.Lib/Requests/IntentRequest.cs index 5d184b2..18a4919 100644 --- a/AlexaSkillsKit.Lib/Speechlet/Requests/IntentRequest.cs +++ b/AlexaSkillsKit.Lib/Requests/IntentRequest.cs @@ -4,7 +4,7 @@ using AlexaSkillsKit.Slu; using Newtonsoft.Json.Linq; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Requests { public class IntentRequest : SpeechletRequest { diff --git a/AlexaSkillsKit.Lib/Speechlet/Requests/LaunchRequest.cs b/AlexaSkillsKit.Lib/Requests/LaunchRequest.cs similarity index 87% rename from AlexaSkillsKit.Lib/Speechlet/Requests/LaunchRequest.cs rename to AlexaSkillsKit.Lib/Requests/LaunchRequest.cs index 007bfa7..1e3bf57 100644 --- a/AlexaSkillsKit.Lib/Speechlet/Requests/LaunchRequest.cs +++ b/AlexaSkillsKit.Lib/Requests/LaunchRequest.cs @@ -2,7 +2,7 @@ using Newtonsoft.Json.Linq; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Requests { public class LaunchRequest : SpeechletRequest { diff --git a/AlexaSkillsKit.Lib/Speechlet/Requests/SessionEndedRequest.cs b/AlexaSkillsKit.Lib/Requests/SessionEndedRequest.cs similarity index 95% rename from AlexaSkillsKit.Lib/Speechlet/Requests/SessionEndedRequest.cs rename to AlexaSkillsKit.Lib/Requests/SessionEndedRequest.cs index ac02d5c..ba0e079 100644 --- a/AlexaSkillsKit.Lib/Speechlet/Requests/SessionEndedRequest.cs +++ b/AlexaSkillsKit.Lib/Requests/SessionEndedRequest.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json.Linq; using System; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Requests { public class SessionEndedRequest : SpeechletRequest { diff --git a/AlexaSkillsKit.Lib/Speechlet/Requests/SessionStartedRequest.cs b/AlexaSkillsKit.Lib/Requests/SessionStartedRequest.cs similarity index 87% rename from AlexaSkillsKit.Lib/Speechlet/Requests/SessionStartedRequest.cs rename to AlexaSkillsKit.Lib/Requests/SessionStartedRequest.cs index 5b8fdf1..92a68be 100644 --- a/AlexaSkillsKit.Lib/Speechlet/Requests/SessionStartedRequest.cs +++ b/AlexaSkillsKit.Lib/Requests/SessionStartedRequest.cs @@ -1,6 +1,6 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Requests { public class SessionStartedRequest : SpeechletRequest { diff --git a/AlexaSkillsKit.Lib/Speechlet/Requests/SpeechletRequest.cs b/AlexaSkillsKit.Lib/Requests/SpeechletRequest.cs similarity index 96% rename from AlexaSkillsKit.Lib/Speechlet/Requests/SpeechletRequest.cs rename to AlexaSkillsKit.Lib/Requests/SpeechletRequest.cs index 1fd2c96..041efaf 100644 --- a/AlexaSkillsKit.Lib/Speechlet/Requests/SpeechletRequest.cs +++ b/AlexaSkillsKit.Lib/Requests/SpeechletRequest.cs @@ -1,11 +1,12 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. using AlexaSkillsKit.Helpers; +using AlexaSkillsKit.Json; using Newtonsoft.Json.Linq; using System; using System.Linq; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Requests { public abstract class SpeechletRequest { diff --git a/AlexaSkillsKit.Lib/Speechlet/Requests/SystemExceptionEncounteredRequest.cs b/AlexaSkillsKit.Lib/Requests/SystemExceptionEncounteredRequest.cs similarity index 87% rename from AlexaSkillsKit.Lib/Speechlet/Requests/SystemExceptionEncounteredRequest.cs rename to AlexaSkillsKit.Lib/Requests/SystemExceptionEncounteredRequest.cs index 247959d..5c85a0a 100644 --- a/AlexaSkillsKit.Lib/Speechlet/Requests/SystemExceptionEncounteredRequest.cs +++ b/AlexaSkillsKit.Lib/Requests/SystemExceptionEncounteredRequest.cs @@ -1,6 +1,7 @@ -using Newtonsoft.Json.Linq; +using AlexaSkillsKit.Speechlet; +using Newtonsoft.Json.Linq; -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Requests { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#system-exceptionencountered diff --git a/AlexaSkillsKit.Lib/Sdk.cs b/AlexaSkillsKit.Lib/Sdk.cs index c8df4fd..e30314e 100644 --- a/AlexaSkillsKit.Lib/Sdk.cs +++ b/AlexaSkillsKit.Lib/Sdk.cs @@ -1,6 +1,5 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using System; using Newtonsoft.Json; namespace AlexaSkillsKit diff --git a/AlexaSkillsKit.Lib/Speechlet/Application.cs b/AlexaSkillsKit.Lib/Speechlet/Application.cs index 40b17fa..11e35dd 100644 --- a/AlexaSkillsKit.Lib/Speechlet/Application.cs +++ b/AlexaSkillsKit.Lib/Speechlet/Application.cs @@ -1,6 +1,5 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using System; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Speechlet diff --git a/AlexaSkillsKit.Lib/AsyncHelpers.cs b/AlexaSkillsKit.Lib/Speechlet/AsyncHelpers.cs similarity index 100% rename from AlexaSkillsKit.Lib/AsyncHelpers.cs rename to AlexaSkillsKit.Lib/Speechlet/AsyncHelpers.cs diff --git a/AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerState.cs b/AlexaSkillsKit.Lib/Speechlet/AudioPlayerState.cs similarity index 100% rename from AlexaSkillsKit.Lib/Interfaces/AudioPlayer/AudioPlayerState.cs rename to AlexaSkillsKit.Lib/Speechlet/AudioPlayerState.cs diff --git a/AlexaSkillsKit.Lib/Speechlet/ISpeechlet.cs b/AlexaSkillsKit.Lib/Speechlet/ISpeechlet.cs index 92960df..ab96d41 100644 --- a/AlexaSkillsKit.Lib/Speechlet/ISpeechlet.cs +++ b/AlexaSkillsKit.Lib/Speechlet/ISpeechlet.cs @@ -1,18 +1,11 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using System; -using AlexaSkillsKit.Authentication; -using AlexaSkillsKit.Json; +using AlexaSkillsKit.Requests; namespace AlexaSkillsKit.Speechlet { public interface ISpeechlet { - bool OnRequestValidation( - SpeechletRequestValidationResult result, DateTime referenceTimeUtc, SpeechletRequestEnvelope requestEnvelope); - - void OnSystemExceptionEncountered(SystemExceptionEncounteredRequest systemRequest, Context context); - SpeechletResponse OnIntent(IntentRequest intentRequest, Session session, Context context); SpeechletResponse OnLaunch(LaunchRequest launchRequest, Session session); void OnSessionStarted(SessionStartedRequest sessionStartedRequest, Session session); diff --git a/AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs b/AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs index 23d9115..cebbd39 100644 --- a/AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs +++ b/AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs @@ -1,19 +1,12 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using System; +using AlexaSkillsKit.Requests; using System.Threading.Tasks; -using AlexaSkillsKit.Authentication; -using AlexaSkillsKit.Json; namespace AlexaSkillsKit.Speechlet { public interface ISpeechletAsync { - Task OnRequestValidationAsync( - SpeechletRequestValidationResult result, DateTime referenceTimeUtc, SpeechletRequestEnvelope requestEnvelope); - - Task OnSystemExceptionEncounteredAsync(SystemExceptionEncounteredRequest systemRequest, Context context); - Task OnIntentAsync(IntentRequest intentRequest, Session session, Context context); Task OnLaunchAsync(LaunchRequest launchRequest, Session session); Task OnSessionStartedAsync(SessionStartedRequest sessionStartedRequest, Session session); diff --git a/AlexaSkillsKit.Lib/Speechlet/ISystemSpeechlet.cs b/AlexaSkillsKit.Lib/Speechlet/ISystemSpeechlet.cs new file mode 100644 index 0000000..21154b4 --- /dev/null +++ b/AlexaSkillsKit.Lib/Speechlet/ISystemSpeechlet.cs @@ -0,0 +1,9 @@ +using AlexaSkillsKit.Requests; + +namespace AlexaSkillsKit.Speechlet +{ + public interface ISystemSpeechlet + { + void OnSystemExceptionEncountered(SystemExceptionEncounteredRequest systemRequest, Context context); + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/ISystemSpeechletAsync.cs b/AlexaSkillsKit.Lib/Speechlet/ISystemSpeechletAsync.cs new file mode 100644 index 0000000..9c04b11 --- /dev/null +++ b/AlexaSkillsKit.Lib/Speechlet/ISystemSpeechletAsync.cs @@ -0,0 +1,10 @@ +using AlexaSkillsKit.Requests; +using System.Threading.Tasks; + +namespace AlexaSkillsKit.Speechlet +{ + public interface ISystemSpeechletAsync + { + Task OnSystemExceptionEncounteredAsync(SystemExceptionEncounteredRequest systemRequest, Context context); + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs b/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs index 53efa0b..7045817 100644 --- a/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs +++ b/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs @@ -1,61 +1,19 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. +using AlexaSkillsKit.Requests; using System; -using System.Net.Http; -using Newtonsoft.Json.Linq; -using AlexaSkillsKit.Authentication; -using AlexaSkillsKit.Json; namespace AlexaSkillsKit.Speechlet { - [Obsolete("Speechlet base class is obselete and will be removed in a future versions of this library. Implement ISpeechlet interface directly instead.")] - public abstract class Speechlet : ISpeechlet + public abstract class Speechlet : SpeechletBase, ISpeechlet { - /// - /// Processes Alexa request AND validates request signature - /// - /// - /// - public virtual HttpResponseMessage GetResponse(HttpRequestMessage httpRequest) { - return (this as ISpeechlet).GetResponse(httpRequest); + protected Speechlet() { + Service.AddStandard(this); } - - /// - /// - /// - /// - /// - public virtual string ProcessRequest(string requestContent) { - return (this as ISpeechlet).ProcessRequest(requestContent); - } - - - /// - /// - /// - /// - /// - public virtual string ProcessRequest(JObject requestJson) { - return (this as ISpeechlet).ProcessRequest(requestJson); - } - - - /// - /// Opportunity to set policy for handling requests with invalid signatures and/or timestamps - /// - /// true if request processing should continue, otherwise false - public virtual bool OnRequestValidation( - SpeechletRequestValidationResult result, DateTime referenceTimeUtc, SpeechletRequestEnvelope requestEnvelope) { - - return result == SpeechletRequestValidationResult.OK; - } - - public abstract void OnSystemExceptionEncountered(SystemExceptionEncounteredRequest systemRequest, Context context); - public abstract SpeechletResponse OnIntent(IntentRequest intentRequest, Session session, Context context); public abstract SpeechletResponse OnLaunch(LaunchRequest launchRequest, Session session); public abstract void OnSessionStarted(SessionStartedRequest sessionStartedRequest, Session session); public abstract void OnSessionEnded(SessionEndedRequest sessionEndedRequest, Session session); } -} +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs index 2a88abd..71ec95e 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs @@ -1,60 +1,16 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using AlexaSkillsKit.Authentication; -using AlexaSkillsKit.Json; -using Newtonsoft.Json.Linq; -using System; -using System.Net.Http; +using AlexaSkillsKit.Requests; using System.Threading.Tasks; namespace AlexaSkillsKit.Speechlet { - [Obsolete("SpeechletAsync base class is obselete and will be removed in a future versions of this library. Implement ISpeechletAsync interface directly instead.")] - public abstract class SpeechletAsync : ISpeechletAsync + public abstract class SpeechletAsync : SpeechletBase, ISpeechletAsync { - /// - /// Processes Alexa request AND validates request signature - /// - /// - /// - public async virtual Task GetResponseAsync(HttpRequestMessage httpRequest) { - return await (this as ISpeechletAsync).GetResponseAsync(httpRequest); + protected SpeechletAsync() { + Service.AddStandard(this); } - - /// - /// Processes Alexa request but does NOT validate request signature - /// - /// - /// - public async virtual Task ProcessRequestAsync(string requestContent) { - return await (this as ISpeechletAsync).ProcessRequestAsync(requestContent); - } - - - /// - /// Processes Alexa request but does NOT validate request signature - /// - /// - /// - public async virtual Task ProcessRequestAsync(JObject requestJson) { - return await (this as ISpeechletAsync).ProcessRequestAsync(requestJson); - } - - - /// - /// Opportunity to set policy for handling requests with invalid signatures and/or timestamps - /// - /// true if request processing should continue, otherwise false - public virtual Task OnRequestValidationAsync( - SpeechletRequestValidationResult result, DateTime referenceTimeUtc, SpeechletRequestEnvelope requestEnvelope) { - - return Task.FromResult(result == SpeechletRequestValidationResult.OK); - } - - - public abstract Task OnSystemExceptionEncounteredAsync(SystemExceptionEncounteredRequest systemRequest, Context context); - public abstract Task OnIntentAsync(IntentRequest intentRequest, Session session, Context context); public abstract Task OnLaunchAsync(LaunchRequest launchRequest, Session session); public abstract Task OnSessionEndedAsync(SessionEndedRequest sessionEndedRequest, Session session); diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs index bc39ddb..b962b67 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs @@ -1,7 +1,5 @@ -using System; +using AlexaSkillsKit.Requests; using System.Threading.Tasks; -using AlexaSkillsKit.Json; -using AlexaSkillsKit.Authentication; namespace AlexaSkillsKit.Speechlet { @@ -13,10 +11,6 @@ public SpeechletAsyncWrapper(ISpeechlet speechlet) { this.speechlet = speechlet; } - public async Task OnSystemExceptionEncounteredAsync(SystemExceptionEncounteredRequest systemRequest, Context context) { - speechlet.OnSystemExceptionEncountered(systemRequest, context); - } - public async Task OnIntentAsync(IntentRequest intentRequest, Session session, Context context) { return speechlet.OnIntent(intentRequest, session, context); } @@ -25,7 +19,6 @@ public async Task OnLaunchAsync(LaunchRequest launchRequest, return speechlet.OnLaunch(launchRequest, session); } - public async Task OnSessionEndedAsync(SessionEndedRequest sessionEndedRequest, Session session) { speechlet.OnSessionEnded(sessionEndedRequest, session); } @@ -33,9 +26,5 @@ public async Task OnSessionEndedAsync(SessionEndedRequest sessionEndedRequest, S public async Task OnSessionStartedAsync(SessionStartedRequest sessionStartedRequest, Session session) { speechlet.OnSessionStarted(sessionStartedRequest, session); } - - public async Task OnRequestValidationAsync(SpeechletRequestValidationResult result, DateTime referenceTimeUtc, SpeechletRequestEnvelope requestEnvelope) { - return speechlet.OnRequestValidation(result, referenceTimeUtc, requestEnvelope); - } } } \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs new file mode 100644 index 0000000..a6324d2 --- /dev/null +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs @@ -0,0 +1,51 @@ +// Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. + +using AlexaSkillsKit.Authentication; +using AlexaSkillsKit.Json; +using Newtonsoft.Json.Linq; +using System; + +namespace AlexaSkillsKit.Speechlet +{ + public abstract class SpeechletBase + { + public SpeechletService Service { get; } = new SpeechletService(); + + public SpeechletBase() { + Service.ValidationHandler = OnRequestValidation; + } + + + /// + /// + /// + /// + /// + public string ProcessRequest(string requestContent) { + var request = SpeechletRequestEnvelope.FromJson(requestContent); + return AsyncHelpers.RunSync(async () => (await Service.ProcessRequestAsync(request))?.ToJson()); + } + + + /// + /// + /// + /// + /// + public string ProcessRequest(JObject requestJson) { + var request = SpeechletRequestEnvelope.FromJson(requestJson); + return AsyncHelpers.RunSync(async () => (await Service.ProcessRequestAsync(request))?.ToJson()); + } + + + /// + /// Opportunity to set policy for handling requests with invalid signatures and/or timestamps + /// + /// true if request processing should continue, otherwise false + public virtual bool OnRequestValidation( + SpeechletRequestValidationResult result, DateTime referenceTimeUtc, SpeechletRequestEnvelope requestEnvelope) { + + return result == SpeechletRequestValidationResult.OK; + } + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletExtensions.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletExtensions.cs deleted file mode 100644 index 53487f3..0000000 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletExtensions.cs +++ /dev/null @@ -1,115 +0,0 @@ -using AlexaSkillsKit.Json; -using Newtonsoft.Json.Linq; -using System.Net.Http; -using System.Threading.Tasks; - -namespace AlexaSkillsKit.Speechlet -{ - public static class SpeechletExtensions - { - public static void UseStandard(this ISpeechlet speechlet) { - new SpeechletAsyncWrapper(speechlet).UseStandard(); - } - - public static void UseStandard(this ISpeechletAsync speechlet) { - var standardResolver = new InterfaceResolver() - .WithDeserializer(nameof(LaunchRequest), (json, subtype) => new LaunchRequest(json)) - .WithDeserializer(nameof(IntentRequest), (json, subtype) => new IntentRequest(json)) - .WithDeserializer(nameof(SessionEndedRequest), (json, subtype) => new SessionEndedRequest(json)); - SpeechletRequestResolver.UseInterface(string.Empty, standardResolver); - - var systemResolver = new InterfaceResolver() - .WithDeserializer("ExceptionEncountered", (json, subtype) => new SystemExceptionEncounteredRequest(json, subtype)); - SpeechletRequestResolver.UseInterface("System", systemResolver); - - SpeechletRequestHandler.UseStandard(speechlet); - } - - /// - /// Processes Alexa request but does NOT validate request signature - /// - /// - /// - public static string ProcessRequest(this ISpeechlet speechlet, string requestContent) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); - return speechlet.ProcessRequest(requestEnvelope)?.ToJson(); - } - - public static string ProcessRequest(this ISpeechletAsync speechlet, string requestContent) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); - return speechlet.ProcessRequest(requestEnvelope)?.ToJson(); - } - - public static async Task ProcessRequestAsync(this ISpeechlet speechlet, string requestContent) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); - return (await speechlet.ProcessRequestAsync(requestEnvelope))?.ToJson(); - } - - public static async Task ProcessRequestAsync(this ISpeechletAsync speechlet, string requestContent) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); - return (await speechlet.ProcessRequestAsync(requestEnvelope))?.ToJson(); - } - - public static string ProcessRequest(this ISpeechlet speechlet, JObject requestJson) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); - return speechlet.ProcessRequest(requestEnvelope)?.ToJson(); - } - - public static string ProcessRequest(this ISpeechletAsync speechlet, JObject requestJson) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); - return speechlet.ProcessRequest(requestEnvelope)?.ToJson(); - } - - public static async Task ProcessRequestAsync(this ISpeechlet speechlet, JObject requestJson) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); - return (await speechlet.ProcessRequestAsync(requestEnvelope))?.ToJson(); - } - - public static async Task ProcessRequestAsync(this ISpeechletAsync speechlet, JObject requestJson) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); - return (await speechlet.ProcessRequestAsync(requestEnvelope))?.ToJson(); - } - - /// - /// Processes Alexa request but does NOT validate request signature - /// - /// - /// - public static SpeechletResponseEnvelope ProcessRequest(this ISpeechlet speechlet, SpeechletRequestEnvelope requestEnvelope) { - return AsyncHelpers.RunSync(() => speechlet.ProcessRequestAsync(requestEnvelope)); - } - - public static SpeechletResponseEnvelope ProcessRequest(this ISpeechletAsync speechlet, SpeechletRequestEnvelope requestEnvelope) { - return AsyncHelpers.RunSync(() => speechlet.ProcessRequestAsync(requestEnvelope)); - } - - public static async Task ProcessRequestAsync(this ISpeechlet speechlet, SpeechletRequestEnvelope requestEnvelope) { - return await new SpeechletRequestHandler(speechlet).DoProcessRequestAsync(requestEnvelope); - } - - public static async Task ProcessRequestAsync(this ISpeechletAsync speechlet, SpeechletRequestEnvelope requestEnvelope) { - return await new SpeechletRequestHandler(speechlet).DoProcessRequestAsync(requestEnvelope); - } - - /// - /// Processes Alexa request AND validates request signature - /// - /// - /// - public static HttpResponseMessage GetResponse(this ISpeechlet speechlet, HttpRequestMessage httpRequest) { - return AsyncHelpers.RunSync(() => speechlet.GetResponseAsync(httpRequest)); - } - - public static HttpResponseMessage GetResponse(this ISpeechletAsync speechlet, HttpRequestMessage httpRequest) { - return AsyncHelpers.RunSync(() => speechlet.GetResponseAsync(httpRequest)); - } - - public static async Task GetResponseAsync(this ISpeechlet speechlet, HttpRequestMessage httpRequest) { - return await new SpeechletRequestHandler(speechlet).GetResponseAsync(httpRequest); - } - - public static async Task GetResponseAsync(this ISpeechletAsync speechlet, HttpRequestMessage httpRequest) { - return await new SpeechletRequestHandler(speechlet).GetResponseAsync(httpRequest); - } - } -} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletRequestHandler.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletRequestHandler.cs deleted file mode 100644 index dd37553..0000000 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletRequestHandler.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Diagnostics; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using AlexaSkillsKit.Json; -using AlexaSkillsKit.Authentication; -using Newtonsoft.Json; -using System.Collections.Generic; - -namespace AlexaSkillsKit.Speechlet -{ - public class SpeechletRequestHandler - { - private static IDictionary>> handlers - = new Dictionary>>(); - - public static void UseInterface(Func> handler) where T: SpeechletRequest { - handlers[typeof(T)] = async (request, session, context) => await handler(request as T, context); - } - - public static void UseStandard(ISpeechletAsync speechletAsync) { - handlers[typeof(LaunchRequest)] = async (request, session, context) => await speechletAsync.OnLaunchAsync(request as LaunchRequest, session); - handlers[typeof(IntentRequest)] = async (request, session, context) => await speechletAsync.OnIntentAsync(request as IntentRequest, session, context); - handlers[typeof(SessionEndedRequest)] = async (request, session, context) => { - await speechletAsync.OnSessionEndedAsync(request as SessionEndedRequest, session); - return null; - }; - handlers[typeof(SystemExceptionEncounteredRequest)] = async (request, session, context) => { - await speechletAsync.OnSystemExceptionEncounteredAsync(request as SystemExceptionEncounteredRequest, context); - return null; - }; - } - - private readonly ISpeechletAsync speechletAsync; - - public SpeechletRequestHandler(ISpeechlet speechlet) { - speechletAsync = new SpeechletAsyncWrapper(speechlet); - } - - public SpeechletRequestHandler(ISpeechletAsync speechletAsync) { - this.speechletAsync = speechletAsync; - } - - /// - /// Processes Alexa request AND validates request signature - /// - /// - /// - public async virtual Task GetResponseAsync(HttpRequestMessage httpRequest) { - SpeechletRequestValidationResult validationResult = SpeechletRequestValidationResult.OK; - - string chainUrl = null; - if (!httpRequest.Headers.Contains(Sdk.SIGNATURE_CERT_URL_REQUEST_HEADER) || - String.IsNullOrEmpty(chainUrl = httpRequest.Headers.GetValues(Sdk.SIGNATURE_CERT_URL_REQUEST_HEADER).First())) { - validationResult = validationResult | SpeechletRequestValidationResult.NoCertHeader; - } - - string signature = null; - if (!httpRequest.Headers.Contains(Sdk.SIGNATURE_REQUEST_HEADER) || - String.IsNullOrEmpty(signature = httpRequest.Headers.GetValues(Sdk.SIGNATURE_REQUEST_HEADER).First())) { - validationResult = validationResult | SpeechletRequestValidationResult.NoSignatureHeader; - } - - var alexaBytes = await httpRequest.Content.ReadAsByteArrayAsync(); - var alexaContent = Encoding.UTF8.GetString(alexaBytes); - Debug.WriteLine(alexaContent); - - // attempt to verify signature only if we were able to locate certificate and signature headers - if (validationResult == SpeechletRequestValidationResult.OK) { - if (!(await SpeechletRequestSignatureVerifier.VerifyRequestSignatureAsync(alexaBytes, signature, chainUrl))) { - validationResult = validationResult | SpeechletRequestValidationResult.InvalidSignature; - } - } - - SpeechletRequestEnvelope alexaRequest = null; - try { - alexaRequest = SpeechletRequestEnvelope.FromJson(alexaContent); - } - catch (Exception ex) - when (ex is JsonReaderException || ex is InvalidCastException || ex is FormatException) { - validationResult = validationResult | SpeechletRequestValidationResult.InvalidJson; - } - - DateTime now = DateTime.UtcNow; // reference time for this request - - // attempt to verify timestamp only if we were able to parse request body - if (alexaRequest != null) { - if (!SpeechletRequestTimestampVerifier.VerifyRequestTimestamp(alexaRequest, now)) { - validationResult = validationResult | SpeechletRequestValidationResult.InvalidTimestamp; - } - } - - if (alexaRequest == null || !await speechletAsync.OnRequestValidationAsync(validationResult, now, alexaRequest)) { - return new HttpResponseMessage(HttpStatusCode.BadRequest) { - ReasonPhrase = validationResult.ToString() - }; - } - - var alexaResponse = await DoProcessRequestAsync(alexaRequest); - var json = alexaResponse?.ToJson(); - Debug.WriteLine(json); - - return (alexaResponse == null) ? - new HttpResponseMessage(HttpStatusCode.InternalServerError) : - new HttpResponseMessage(HttpStatusCode.OK) { - Content = new StringContent(json, Encoding.UTF8, "application/json") - }; - } - - - /// - /// - /// - /// - /// - public async Task DoProcessRequestAsync(SpeechletRequestEnvelope requestEnvelope) { - Session session = requestEnvelope.Session; - var context = requestEnvelope.Context; - var request = requestEnvelope.Request; - ISpeechletResponse response = null; - - if (session != null) { - // Do session management prior to calling OnSessionStarted and OnIntentAsync - // to allow dev to change session values if behavior is not desired - DoSessionManagement(request as IntentRequest, session); - - if (session.IsNew) { - await speechletAsync.OnSessionStartedAsync(new SessionStartedRequest(request), session); - } - } - - foreach (var pair in handlers) { - if (pair.Key.IsAssignableFrom(request.GetType())) { - response = await pair.Value(request, session, context); - break; - } - } - - var responseEnvelope = new SpeechletResponseEnvelope { - Version = requestEnvelope.Version, - Response = response, - SessionAttributes = session?.Attributes - }; - - return responseEnvelope; - } - - - /// - /// - /// - private void DoSessionManagement(IntentRequest request, Session session) { - if (request == null) return; - - if (session.IsNew) { - session.Attributes[Session.INTENT_SEQUENCE] = request.Intent.Name; - } - else { - // if the session was started as a result of a launch request - // a first intent isn't yet set, so set it to the current intent - if (!session.Attributes.ContainsKey(Session.INTENT_SEQUENCE)) { - session.Attributes[Session.INTENT_SEQUENCE] = request.Intent.Name; - } - else { - session.Attributes[Session.INTENT_SEQUENCE] += Session.SEPARATOR + request.Intent.Name; - } - } - - // Auto-session management: copy all slot values from current intent into session - foreach (var slot in request.Intent.Slots.Values) { - if (!String.IsNullOrEmpty(slot.Value)) session.Attributes[slot.Name] = slot.Value; - } - } - } -} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs new file mode 100644 index 0000000..71e3074 --- /dev/null +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs @@ -0,0 +1,138 @@ +using AlexaSkillsKit.Authentication; +using AlexaSkillsKit.Json; +using AlexaSkillsKit.Requests; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace AlexaSkillsKit.Speechlet +{ + public class SpeechletService + { + private IDictionary>> handlers + = new Dictionary>>(); + + public Func ValidationHandler { get; set; } + + public void AddHandler(Func> handler) where T : SpeechletRequest { + handlers[typeof(T)] = async (request, session, context) => await handler(request as T, session, context); + } + + public void AddHandler(Func> handler) where T : SpeechletRequest { + handlers[typeof(T)] = async (request, session, context) => await handler(request as T, context); + } + + public async Task GetRequestAsync(string content, string chainUrl, string signature) { + var validationResult = SpeechletRequestValidationResult.OK; + + if (string.IsNullOrEmpty(chainUrl)) { + validationResult |= SpeechletRequestValidationResult.NoCertHeader; + } + + if (string.IsNullOrEmpty(signature)) { + validationResult |= SpeechletRequestValidationResult.NoSignatureHeader; + } + + var alexaBytes = Encoding.UTF8.GetBytes(content); + + // attempt to verify signature only if we were able to locate certificate and signature headers + if (validationResult == SpeechletRequestValidationResult.OK) { + if (!(await SpeechletRequestSignatureVerifier.VerifyRequestSignatureAsync(alexaBytes, signature, chainUrl))) { + validationResult |= SpeechletRequestValidationResult.InvalidSignature; + } + } + + SpeechletRequestEnvelope alexaRequest = null; + try { + alexaRequest = SpeechletRequestEnvelope.FromJson(content); + } + catch (SpeechletValidationException ex) { + validationResult |= ex.ValidationResult; + } + catch (Exception ex) + when (ex is JsonReaderException || ex is InvalidCastException || ex is FormatException) { + validationResult |= SpeechletRequestValidationResult.InvalidJson; + } + + DateTime now = DateTime.UtcNow; // reference time for this request + + // attempt to verify timestamp only if we were able to parse request body + if (alexaRequest != null) { + if (!SpeechletRequestTimestampVerifier.VerifyRequestTimestamp(alexaRequest, now)) { + validationResult |= SpeechletRequestValidationResult.InvalidTimestamp; + } + } + + var success = ValidationHandler?.Invoke(validationResult, now, alexaRequest) ?? (validationResult == SpeechletRequestValidationResult.OK); + + if (!success) { + throw new SpeechletValidationException(validationResult); + } + + return alexaRequest; + } + + /// + /// + /// + /// + /// + public async Task ProcessRequestAsync(SpeechletRequestEnvelope requestEnvelope) { + Session session = requestEnvelope.Session; + var context = requestEnvelope.Context; + var request = requestEnvelope.Request; + ISpeechletResponse response = null; + + if (session != null) { + // Do session management prior to calling OnSessionStarted and OnIntentAsync + // to allow dev to change session values if behavior is not desired + DoSessionManagement(request as IntentRequest, session); + + if (session.IsNew && handlers.ContainsKey(typeof(SessionStartedRequest))) { + await handlers[typeof(SessionStartedRequest)].Invoke(new SessionStartedRequest(request), session, context); + } + } + + foreach (var pair in handlers) { + if (pair.Key.GetTypeInfo().IsAssignableFrom(request.GetType().GetTypeInfo())) { + response = await pair.Value(request, session, context); + break; + } + } + + var responseEnvelope = new SpeechletResponseEnvelope { + Version = requestEnvelope.Version, + Response = response, + SessionAttributes = session?.Attributes + }; + + return responseEnvelope; + } + + private void DoSessionManagement(IntentRequest request, Session session) { + if (request == null) return; + + if (session.IsNew) { + session.Attributes[Session.INTENT_SEQUENCE] = request.Intent.Name; + } + else { + // if the session was started as a result of a launch request + // a first intent isn't yet set, so set it to the current intent + if (!session.Attributes.ContainsKey(Session.INTENT_SEQUENCE)) { + session.Attributes[Session.INTENT_SEQUENCE] = request.Intent.Name; + } + else { + session.Attributes[Session.INTENT_SEQUENCE] += Session.SEPARATOR + request.Intent.Name; + } + } + + // Auto-session management: copy all slot values from current intent into session + foreach (var slot in request.Intent.Slots.Values) { + if (!String.IsNullOrEmpty(slot.Value)) session.Attributes[slot.Name] = slot.Value; + } + } + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs new file mode 100644 index 0000000..c4f77f0 --- /dev/null +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs @@ -0,0 +1,40 @@ +using AlexaSkillsKit.Json; +using AlexaSkillsKit.Requests; + +namespace AlexaSkillsKit.Speechlet +{ + public static class SpeechletServiceExtensions + { + public static void AddStandard(this SpeechletService service, ISpeechletAsync speechlet) { + service.AddHandler(async (request, session, context) => + await speechlet.OnLaunchAsync(request, session)); + + service.AddHandler(async (request, session, context) => + await speechlet.OnIntentAsync(request, session, context)); + + service.AddHandler(async (request, session, context) => { + await speechlet.OnSessionEndedAsync(request, session); + return null; + }); + + SpeechletRequestResolver.AddStandard(); + } + + public static void AddStandard(this SpeechletService service, ISpeechlet speechlet) { + service.AddStandard(new SpeechletAsyncWrapper(speechlet)); + } + + public static void AddSystem(this SpeechletService service, ISystemSpeechletAsync speechlet) { + service.AddHandler(async (request, session, context) => { + await speechlet.OnSystemExceptionEncounteredAsync(request, context); + return null; + }); + + SpeechletRequestResolver.AddSystem(); + } + + public static void AddSystem(this SpeechletService service, ISystemSpeechlet speechlet) { + service.AddSystem(new SystemSpeechletAsyncWrapper(speechlet)); + } + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletValidationException.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletValidationException.cs new file mode 100644 index 0000000..29afd4b --- /dev/null +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletValidationException.cs @@ -0,0 +1,24 @@ +// Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. + +using AlexaSkillsKit.Authentication; +using System; + +namespace AlexaSkillsKit.Speechlet +{ + public class SpeechletValidationException: SpeechletException + { + public SpeechletRequestValidationResult ValidationResult { get; } + + public SpeechletValidationException(SpeechletRequestValidationResult validationResult) : base() { + ValidationResult = validationResult; + } + + public SpeechletValidationException(SpeechletRequestValidationResult validationResult, string message) : base(message) { + ValidationResult = validationResult; + } + + public SpeechletValidationException(SpeechletRequestValidationResult validationResult, string message, Exception cause) : base(message, cause) { + ValidationResult = validationResult; + } + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/SupportedInterfaces.cs b/AlexaSkillsKit.Lib/Speechlet/SupportedInterfaces.cs index 2b2b8e6..74fc8ac 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SupportedInterfaces.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SupportedInterfaces.cs @@ -1,8 +1,7 @@ using System.Collections.Generic; using Newtonsoft.Json.Linq; using System.Linq; -using AlexaSkillsKit.Interfaces.Display; -using AlexaSkillsKit.Interfaces.AudioPlayer; +using AlexaSkillsKit.Json; namespace AlexaSkillsKit.Speechlet { @@ -11,14 +10,6 @@ namespace AlexaSkillsKit.Speechlet /// public class SupportedInterfaces : Dictionary { - /// - /// Register supported interfaces for deserialization - /// - static SupportedInterfaces() { - Deserializer.RegisterDeserializer("Display", DisplayInterface.FromJson); - Deserializer.RegisterDeserializer("AudioPlayer", AudioPlayerInterface.FromJson); - } - public static SupportedInterfaces FromJson(JObject json) { if (json == null) return null; diff --git a/AlexaSkillsKit.Lib/Speechlet/SystemSpeechletAsyncWrapper.cs b/AlexaSkillsKit.Lib/Speechlet/SystemSpeechletAsyncWrapper.cs new file mode 100644 index 0000000..d8d1584 --- /dev/null +++ b/AlexaSkillsKit.Lib/Speechlet/SystemSpeechletAsyncWrapper.cs @@ -0,0 +1,18 @@ +using AlexaSkillsKit.Requests; +using System.Threading.Tasks; + +namespace AlexaSkillsKit.Speechlet +{ + public class SystemSpeechletAsyncWrapper: ISystemSpeechletAsync + { + private readonly ISystemSpeechlet speechlet; + + public SystemSpeechletAsyncWrapper(ISystemSpeechlet speechlet) { + this.speechlet = speechlet; + } + + public async Task OnSystemExceptionEncounteredAsync(SystemExceptionEncounteredRequest systemRequest, Context context) { + speechlet.OnSystemExceptionEncountered(systemRequest, context); + } + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/app.config b/AlexaSkillsKit.Lib/app.config deleted file mode 100644 index 195db1f..0000000 --- a/AlexaSkillsKit.Lib/app.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/packages.config b/AlexaSkillsKit.Lib/packages.config deleted file mode 100644 index 284ea23..0000000 --- a/AlexaSkillsKit.Lib/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/AlexaSkillsKit.Sample/AlexaSkillsKit.Sample.csproj b/AlexaSkillsKit.Sample/AlexaSkillsKit.Sample.csproj index 254d2e0..794e7ea 100644 --- a/AlexaSkillsKit.Sample/AlexaSkillsKit.Sample.csproj +++ b/AlexaSkillsKit.Sample/AlexaSkillsKit.Sample.csproj @@ -23,6 +23,8 @@ ..\ true + + true @@ -107,9 +109,8 @@ False ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll - True + + ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll ..\packages\NLog.3.2.1\lib\net45\NLog.dll @@ -120,10 +121,9 @@ + - - ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll True @@ -131,6 +131,9 @@ + + + ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll True @@ -170,7 +173,6 @@ ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Razor.dll True - @@ -179,6 +181,7 @@ + ..\packages\WebGrease.1.6.0\lib\WebGrease.dll True @@ -312,9 +315,21 @@ - - {0ec882a8-aaca-4bd5-b449-72f20fdb8586} - AlexaSkillsKit.Lib + + {742331b9-5325-4f3f-b41f-ec41dff9bd0c} + AlexaSkillsKit.Http + + + {6224BE94-2A4B-4AB2-BA8A-4BD3DF47FC4F} + AlexaSkillsKit.Interfaces.Display + + + {1517FE44-37AF-4585-AF54-0A20F222CFCB} + AlexaSkillsKit.Interfaces.VideoApp + + + {01b7fa9f-5f29-46fa-8e27-b731a2a95876} + AlexaSkillsKit.Core diff --git a/AlexaSkillsKit.Sample/Speechlet/AlexaController.cs b/AlexaSkillsKit.Sample/Speechlet/AlexaController.cs index d3cd365..d833a8c 100644 --- a/AlexaSkillsKit.Sample/Speechlet/AlexaController.cs +++ b/AlexaSkillsKit.Sample/Speechlet/AlexaController.cs @@ -1,6 +1,7 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using AlexaSkillsKit.Speechlet; +using AlexaSkillsKit.Http; +using AlexaSkillsKit.Interfaces.Display; using System.Net.Http; using System.Web.Http; @@ -12,7 +13,6 @@ public class AlexaController : ApiController [HttpPost] public HttpResponseMessage SampleSession() { var speechlet = new SampleSessionSpeechlet(); - speechlet.UseStandard(); return speechlet.GetResponse(Request); } } diff --git a/AlexaSkillsKit.Sample/Speechlet/SampleSessionSpeechlet.cs b/AlexaSkillsKit.Sample/Speechlet/SampleSessionSpeechlet.cs index 7eb8e7e..30ed7f1 100644 --- a/AlexaSkillsKit.Sample/Speechlet/SampleSessionSpeechlet.cs +++ b/AlexaSkillsKit.Sample/Speechlet/SampleSessionSpeechlet.cs @@ -1,15 +1,16 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using System; -using System.Collections.Generic; -using NLog; -using AlexaSkillsKit.Speechlet; -using AlexaSkillsKit.Slu; -using AlexaSkillsKit.UI; using AlexaSkillsKit.Interfaces.Display; -using AlexaSkillsKit.Interfaces.VideoApp; using AlexaSkillsKit.Interfaces.Display.Directives; +using AlexaSkillsKit.Interfaces.VideoApp; using AlexaSkillsKit.Interfaces.VideoApp.Directives; +using AlexaSkillsKit.Requests; +using AlexaSkillsKit.Slu; +using AlexaSkillsKit.Speechlet; +using AlexaSkillsKit.UI; +using NLog; +using System; +using System.Collections.Generic; namespace Sample.Controllers { @@ -21,11 +22,6 @@ public class SampleSessionSpeechlet : Speechlet private const string NAME_KEY = "name"; private const string NAME_SLOT = "Name"; - - public override void OnSystemExceptionEncountered(SystemExceptionEncounteredRequest systemRequest, Context context) { - throw new NotImplementedException(); - } - public override void OnSessionStarted(SessionStartedRequest request, Session session) { Log.Info("OnSessionStarted requestId={0}, sessionId={1}", request.RequestId, session.SessionId); } diff --git a/AlexaSkillsKit.Sample/Web.config b/AlexaSkillsKit.Sample/Web.config index 8f0df6e..5d6d1b0 100644 --- a/AlexaSkillsKit.Sample/Web.config +++ b/AlexaSkillsKit.Sample/Web.config @@ -5,13 +5,21 @@ --> -
- + + - + + @@ -21,19 +29,19 @@ - - + - + + - - - - + + + + @@ -52,7 +60,7 @@ - + @@ -98,4 +106,4 @@ - + \ No newline at end of file diff --git a/AlexaSkillsKit.Sample/packages.config b/AlexaSkillsKit.Sample/packages.config index abdc168..eea55aa 100644 --- a/AlexaSkillsKit.Sample/packages.config +++ b/AlexaSkillsKit.Sample/packages.config @@ -30,7 +30,7 @@ - + diff --git a/AlexaSkillsKit.Tests/AlexaSkillsKit.Tests.csproj b/AlexaSkillsKit.Tests/AlexaSkillsKit.Tests.csproj index 98fd3e9..29f57bf 100644 --- a/AlexaSkillsKit.Tests/AlexaSkillsKit.Tests.csproj +++ b/AlexaSkillsKit.Tests/AlexaSkillsKit.Tests.csproj @@ -39,9 +39,8 @@ 4 - - ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll - True + + ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll @@ -88,9 +87,9 @@ - - {0ec882a8-aaca-4bd5-b449-72f20fdb8586} - AlexaSkillsKit.Lib + + {01b7fa9f-5f29-46fa-8e27-b731a2a95876} + AlexaSkillsKit.Core diff --git a/AlexaSkillsKit.Tests/Authentication/SignatureVerifierTests.cs b/AlexaSkillsKit.Tests/Authentication/SignatureVerifierTests.cs index 19eb3ea..8a89316 100644 --- a/AlexaSkillsKit.Tests/Authentication/SignatureVerifierTests.cs +++ b/AlexaSkillsKit.Tests/Authentication/SignatureVerifierTests.cs @@ -1,7 +1,5 @@ -using System; +using AlexaSkillsKit.Authentication; using Xunit; -using AlexaSkillsKit; -using AlexaSkillsKit.Authentication; namespace AlexaSkillsKit.Tests { diff --git a/AlexaSkillsKit.Tests/packages.config b/AlexaSkillsKit.Tests/packages.config index 9219867..42f9093 100644 --- a/AlexaSkillsKit.Tests/packages.config +++ b/AlexaSkillsKit.Tests/packages.config @@ -1,6 +1,6 @@  - + diff --git a/AlexaSkillsKit.sln b/AlexaSkillsKit.sln index 3d68899..a943d3e 100644 --- a/AlexaSkillsKit.sln +++ b/AlexaSkillsKit.sln @@ -1,10 +1,8 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2010 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Lib", "AlexaSkillsKit.Lib\AlexaSkillsKit.Lib.csproj", "{0EC882A8-AACA-4BD5-B449-72F20FDB8586}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Sample", "AlexaSkillsKit.Sample\AlexaSkillsKit.Sample.csproj", "{9FDEF793-5832-4D37-B0D5-62C55AB1C12E}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{D35AD75F-7926-41CB-8C8D-BB36370F3E76}" @@ -16,16 +14,24 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{D35AD7 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Tests", "AlexaSkillsKit.Tests\AlexaSkillsKit.Tests.csproj", "{D0125A95-FD63-4A33-9220-206D4A1A14F0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Http", "AlexaSkillsKit.Http\AlexaSkillsKit.Http.csproj", "{742331B9-5325-4F3F-B41F-EC41DFF9BD0C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AlexaSkillsKit.Core", "AlexaSkillsKit.Lib\AlexaSkillsKit.Core.csproj", "{01B7FA9F-5F29-46FA-8E27-B731A2A95876}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Interfaces.AudioPlayer", "AlexaSkillsKit.Interfaces.AudioPlayer\AlexaSkillsKit.Interfaces.AudioPlayer.csproj", "{2D25EE87-D293-4A62-BCEC-AACB4099ED9E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Interfaces.Display", "AlexaSkillsKit.Interfaces.Display\AlexaSkillsKit.Interfaces.Display.csproj", "{6224BE94-2A4B-4AB2-BA8A-4BD3DF47FC4F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Interfaces.Dialog", "AlexaSkillsKit.Interfaces.Dialog\AlexaSkillsKit.Interfaces.Dialog.csproj", "{39F798E3-70FE-419A-AF58-E0A181A42A40}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Interfaces.VideoApp", "AlexaSkillsKit.Interfaces.VideoApp\AlexaSkillsKit.Interfaces.VideoApp.csproj", "{1517FE44-37AF-4585-AF54-0A20F222CFCB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0EC882A8-AACA-4BD5-B449-72F20FDB8586}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0EC882A8-AACA-4BD5-B449-72F20FDB8586}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0EC882A8-AACA-4BD5-B449-72F20FDB8586}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0EC882A8-AACA-4BD5-B449-72F20FDB8586}.Release|Any CPU.Build.0 = Release|Any CPU {9FDEF793-5832-4D37-B0D5-62C55AB1C12E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9FDEF793-5832-4D37-B0D5-62C55AB1C12E}.Debug|Any CPU.Build.0 = Debug|Any CPU {9FDEF793-5832-4D37-B0D5-62C55AB1C12E}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -34,8 +40,35 @@ Global {D0125A95-FD63-4A33-9220-206D4A1A14F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {D0125A95-FD63-4A33-9220-206D4A1A14F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {D0125A95-FD63-4A33-9220-206D4A1A14F0}.Release|Any CPU.Build.0 = Release|Any CPU + {742331B9-5325-4F3F-B41F-EC41DFF9BD0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {742331B9-5325-4F3F-B41F-EC41DFF9BD0C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {742331B9-5325-4F3F-B41F-EC41DFF9BD0C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {742331B9-5325-4F3F-B41F-EC41DFF9BD0C}.Release|Any CPU.Build.0 = Release|Any CPU + {01B7FA9F-5F29-46FA-8E27-B731A2A95876}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {01B7FA9F-5F29-46FA-8E27-B731A2A95876}.Debug|Any CPU.Build.0 = Debug|Any CPU + {01B7FA9F-5F29-46FA-8E27-B731A2A95876}.Release|Any CPU.ActiveCfg = Release|Any CPU + {01B7FA9F-5F29-46FA-8E27-B731A2A95876}.Release|Any CPU.Build.0 = Release|Any CPU + {2D25EE87-D293-4A62-BCEC-AACB4099ED9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D25EE87-D293-4A62-BCEC-AACB4099ED9E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D25EE87-D293-4A62-BCEC-AACB4099ED9E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D25EE87-D293-4A62-BCEC-AACB4099ED9E}.Release|Any CPU.Build.0 = Release|Any CPU + {6224BE94-2A4B-4AB2-BA8A-4BD3DF47FC4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6224BE94-2A4B-4AB2-BA8A-4BD3DF47FC4F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6224BE94-2A4B-4AB2-BA8A-4BD3DF47FC4F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6224BE94-2A4B-4AB2-BA8A-4BD3DF47FC4F}.Release|Any CPU.Build.0 = Release|Any CPU + {39F798E3-70FE-419A-AF58-E0A181A42A40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39F798E3-70FE-419A-AF58-E0A181A42A40}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39F798E3-70FE-419A-AF58-E0A181A42A40}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39F798E3-70FE-419A-AF58-E0A181A42A40}.Release|Any CPU.Build.0 = Release|Any CPU + {1517FE44-37AF-4585-AF54-0A20F222CFCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1517FE44-37AF-4585-AF54-0A20F222CFCB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1517FE44-37AF-4585-AF54-0A20F222CFCB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1517FE44-37AF-4585-AF54-0A20F222CFCB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {802C7A13-5ADB-4355-91E6-AEF17F26AB36} + EndGlobalSection EndGlobal From 3781726018f838abf69c4dbd94e16da5cb0cd332 Mon Sep 17 00:00:00 2001 From: Sergey Greenko Date: Sun, 17 Dec 2017 22:55:17 +0200 Subject: [PATCH 05/16] Readme update initiated --- AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs | 2 +- README.md | 101 ++++++++++++++++-- 2 files changed, 95 insertions(+), 8 deletions(-) diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs index a6324d2..19aca48 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs @@ -7,7 +7,7 @@ namespace AlexaSkillsKit.Speechlet { - public abstract class SpeechletBase + public class SpeechletBase { public SpeechletService Service { get; } = new SpeechletService(); diff --git a/README.md b/README.md index 9b584f6..a4cb857 100644 --- a/README.md +++ b/README.md @@ -3,24 +3,41 @@ * handles the (de)serialization of Alexa requests & responses into easy-to-use object models * verifies authenticity of the request by validating its signature and timestamp * code-reviewed and vetted by Amazon (Alexa skills written using this library passed certification) +:new: * supports following interfaces: +** [AudioPlayer](https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html) +** [PlaybackController](https://developer.amazon.com/docs/custom-skills/playback-controller-interface-reference.html) +** [Display](https://developer.amazon.com/docs/custom-skills/display-interface-reference.html) +** [Dialog](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html) +** [VideoApp](https://developer.amazon.com/docs/custom-skills/videoapp-interface-reference.html) Beyond the functionality in Amazon's AlexaSkillsKit for Java, AlexaSkillsKit.NET: * performs automatic session management so you can easily [build conversational Alexa apps](https://freebusy.io/blog/building-conversational-alexa-apps-for-amazon-echo) - +:new: * can be extended to support custom and new coming interfaces (see [Advanced]() section) This library was originally developed for and is in use at https://freebusy.io -This library is available as a NuGet package at https://www.nuget.org/packages/AlexaSkillsKit.NET/ +Library is currently available as a single NuGet package: https://www.nuget.org/packages/AlexaSkillsKit.Net/ + +Extensible version will be available as following NuGet packages: +* https://www.nuget.org/packages/AlexaSkillsKit.Core/ - core library +* https://www.nuget.org/packages/AlexaSkillsKit.Http/ - System.Net.Http provider extentions +* https://www.nuget.org/packages/AlexaSkillsKit.AspNetCore/ - ASP.Net Core Web API provider extentions +* https://www.nuget.org/packages/AlexaSkillsKit.Interfaces.AudioPlayer/ - adds support of AudioPlayer and PlaybackController interfaces +* https://www.nuget.org/packages/AlexaSkillsKit.Interfaces.Dialog/ - adds support of Dialog interface +* https://www.nuget.org/packages/AlexaSkillsKit.Interfaces.Display/ - adds support of Display interface +* https://www.nuget.org/packages/AlexaSkillsKit.Interfaces.VideoApp/ - adds support of VideoApp interface + # How To Use ### 1. Set up your development environment Read [Getting started with Alexa App development for Amazon Echo using .NET on Windows](https://freebusy.io/blog/getting-started-with-alexa-app-development-for-amazon-echo-using-dot-net) +(only describes .Net Framework 4.x setup) ### 2. Implement your skill as a "Speechlet" -If your Alexa skill does any kind of I/O and assuming you're building on top of .NET Framework 4.5 it's recommended that you derive your app from the abstract SpeechletAsync and implement these methods as defined by ISpeechletAsync +If your Alexa skill does any kind of asynchronous I/O, it's recommended that you derive your app from the abstract `SpeechletAsync` and implement these methods as defined by `ISpeechletAsync`: ```csharp public interface ISpeechletAsync @@ -31,8 +48,8 @@ public interface ISpeechletAsync Task OnSessionEndedAsync(SessionEndedRequest sessionEndedRequest, Session session); } ``` - -Or derive your app from the abstract Speechlet and implement these methods as defined by ISpeechlet. + +Alternatively you can derive from synchronous `Speechlet` abstract class and implement these methods as defined by `ISpeechlet`: ```csharp public interface ISpeechlet @@ -52,14 +69,74 @@ The Sample app is using ASP.NET 4.5 WebApi 2 so wiring-up requests & responses f *Note: sample project is generated from the ASP.NET 4.5 WebApi 2 template so it includes a lot of functionality that's not directly related to Alexa Speechlets, but it does make make for a complete Web API project.* -Alternatively you can host your app and the AlexaSkillsKit.NET library in any other web service framework like ServiceStack. +Alternatively you can host your app and the AlexaSkillsKit.NET library in any other web service framework like ServiceStack, ASP.NET Core Web API, Azure Function App, etc. + +# How it works + +Starting from version 2.0 AlexaSkillKit provides framework agnostic way to handle incoming Alexa requests. +More over it is refactored, so that Skill authors can now extend library with newly coming not yet natively supported external interfaces and register them in the core library the same way as all existing interfaces are implemented. + +Here is detailed explanation of how requests are handled by the library. + +### SpeechletService class +To handle Alexa requests an instance of `SpeechletService` has to be created. This is the main entry point for all operations involved in handling incoming requests. +For convenience and backward compatibility with earlier library versions both `Speechlet` and `SpeechletAsync` now have built-in `SpeechletService` instance and wrap all most common operations with it. +Skill authors can access their internal `SpeechletService` through `Service` property. + +### Parsing request + +When new request is recieved, it first needs to be parced from json string into object model represented by `SpeechletRequestEnvelope` class. +`Task SpeechletService.GetRequestAsync(string content, string chainUrl, string signature)` method is used for this. +Request headers and body validation also takes place during this step. The `SpeechletValidationException` is produced in case of any validation error. + +Skill authors can set SpeechletService.ValidationHandler property to have better control on when exception should be thrown, or to throw custom exceptions instead. +For backward compatibility, the `Speechlet` and `SpeechletAsync` helper abstract classes set `ValidationHandler` property of their internal service to virtual `OnRequestValidation` method. +Setting `ValidationHandler` property will override this behavior. See [Override request validation policy](#Override request validation policy) for more details on request validation. +Request validation can be omitted by directly calling one of `SpeechletRequestEnvelope.FromJson` static methods. + +`SpeechletRequestEnvelope` consists of `Version`, `Session`, `Context` and `Request` fields. See [Context](#Context) for more details on parsing context. + +Only version "1.0" of Alexa Skill API is supported. Otherwise `SpeechletValidationException` with `SpeechletRequestValidationResult.InvalidVersion` is thrown. +Same is true, when calling `SpeechletRequestEnvelope.FromJson` methods directly. + +There are a lot of different request types available for Alexa Skills. +Standard requests have simple type names: "LaunchRequest", "IntentRequest", "SessionEndedRequest". +All other requests are related to specific interfaces and their request type name consists of interface name and request subtype name separated by "." sign: "System.ExceptionEncountered", "Dialog.Delegate" and so on. -## Advanced +`SpeechletRequestResolver` static class is used to deserialize `request` json field to appropriate subclass of `SpeechletRequest` base class. +By default, it has no knowledge to which class each request type should be deserialized. +`SpeechletRequestResolver` has `AddInterface` method to bind interface name, with specific `InterfaceResolver`. +`SpeechletRequestResolver` provides `AddStandard` method to register all standard requests and `AddSystem` to register `System.ExceptionEncountered` request. +Both `Speechlet` and `SpeechletAsync` are calling `SpeechletRequestResolver.AddStandard` during initialization. + +Each interface library that provide own requests is intended to provide method to register those requests in `SpeechletRequestResolver`. +So do `AlexaSkillsKit.Interfaces.Display` in its `void AddDisplay(this SpeechletService service, IDisplaySpeechletAsync speechlet)` extention method +and `AlexaSkillsKit.Interfaces.AudioPlayer` in `void AddAudioPlayer(this SpeechletService service, IAudioPlayerSpeechletAsync speechlet)`. +For more information on using external interfaces see [Use external interfaces](#Use external interfaces). +For more information on registering custom interfaces see [Implement external interface](#Implement external interface). + +### Processing request + +//TODO + +# Advanced + +### Use external interfaces + +//TODO + +### Implement external interface + +//TODO ### Override request validation policy +//TODO + By default, requests with missing or invalid signatures, or with missing or too old timestamps, are rejected. You can override the request validation policy if you'd like not to reject the request in certain conditions and/or to log validation failures. +If you are deriving from SpeechletAsync or Speechlet, simply override `OnRequestValidation` method as follows: + ```csharp /// /// return true if you want request to be processed, otherwise false @@ -104,3 +181,13 @@ public override bool OnRequestValidation( } } ``` + +### Use Speechlet Service directly + +Internally both SpeechletAsync and Speechlet abstract classes are derived from SpeechletBase class. SpeechletBase creates and exposes SpeechletService object used for request handling. + +SpeechletService + +To have better control over request handling you can use SpeechletService directly or even derive from it. + +In this case you need \ No newline at end of file From 5ad8484922518b77d14a1845d3c8e9f6a6ee587a Mon Sep 17 00:00:00 2001 From: Sergey Greenko Date: Sun, 17 Dec 2017 23:21:59 +0200 Subject: [PATCH 06/16] Fix minor formatting issues in Readme --- README.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a4cb857..2fa3379 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,16 @@ * handles the (de)serialization of Alexa requests & responses into easy-to-use object models * verifies authenticity of the request by validating its signature and timestamp * code-reviewed and vetted by Amazon (Alexa skills written using this library passed certification) -:new: * supports following interfaces: -** [AudioPlayer](https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html) -** [PlaybackController](https://developer.amazon.com/docs/custom-skills/playback-controller-interface-reference.html) -** [Display](https://developer.amazon.com/docs/custom-skills/display-interface-reference.html) -** [Dialog](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html) -** [VideoApp](https://developer.amazon.com/docs/custom-skills/videoapp-interface-reference.html) +* :new: supports following interfaces: + * [AudioPlayer](https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html) + * [PlaybackController](https://developer.amazon.com/docs/custom-skills/playback-controller-interface-reference.html) + * [Display](https://developer.amazon.com/docs/custom-skills/display-interface-reference.html) + * [Dialog](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html) + * [VideoApp](https://developer.amazon.com/docs/custom-skills/videoapp-interface-reference.html) Beyond the functionality in Amazon's AlexaSkillsKit for Java, AlexaSkillsKit.NET: * performs automatic session management so you can easily [build conversational Alexa apps](https://freebusy.io/blog/building-conversational-alexa-apps-for-amazon-echo) -:new: * can be extended to support custom and new coming interfaces (see [Advanced]() section) +* :new: can be extended to support custom and new coming interfaces (see [Advanced]() section) This library was originally developed for and is in use at https://freebusy.io @@ -33,7 +33,9 @@ Extensible version will be available as following NuGet packages: ### 1. Set up your development environment Read [Getting started with Alexa App development for Amazon Echo using .NET on Windows](https://freebusy.io/blog/getting-started-with-alexa-app-development-for-amazon-echo-using-dot-net) -(only describes .Net Framework 4.x setup) +for information on how you can setup you local certificate. + +Note, that if you are hosting your API in Amazon Lambda, Azure Function, Azure Web App or other well-known cloud service, you can use parent domain certificate instead of providing your own. ### 2. Implement your skill as a "Speechlet" From 105e83fe9480bc726fcf700f00e7ee028ee6e876 Mon Sep 17 00:00:00 2001 From: Sergey Greenko Date: Sun, 17 Dec 2017 23:26:36 +0200 Subject: [PATCH 07/16] Fix links in Readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2fa3379..99b0940 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Beyond the functionality in Amazon's AlexaSkillsKit for Java, AlexaSkillsKit.NET: * performs automatic session management so you can easily [build conversational Alexa apps](https://freebusy.io/blog/building-conversational-alexa-apps-for-amazon-echo) -* :new: can be extended to support custom and new coming interfaces (see [Advanced]() section) +* :new: can be extended to support custom and new coming interfaces (see [Implement external interface](#Implement%20external%20interface) section) This library was originally developed for and is in use at https://freebusy.io @@ -93,7 +93,7 @@ Request headers and body validation also takes place during this step. The `Spee Skill authors can set SpeechletService.ValidationHandler property to have better control on when exception should be thrown, or to throw custom exceptions instead. For backward compatibility, the `Speechlet` and `SpeechletAsync` helper abstract classes set `ValidationHandler` property of their internal service to virtual `OnRequestValidation` method. -Setting `ValidationHandler` property will override this behavior. See [Override request validation policy](#Override request validation policy) for more details on request validation. +Setting `ValidationHandler` property will override this behavior. See [Override request validation policy](#Override%20request%20validation%20policy) for more details on request validation. Request validation can be omitted by directly calling one of `SpeechletRequestEnvelope.FromJson` static methods. `SpeechletRequestEnvelope` consists of `Version`, `Session`, `Context` and `Request` fields. See [Context](#Context) for more details on parsing context. @@ -114,8 +114,8 @@ Both `Speechlet` and `SpeechletAsync` are calling `SpeechletRequestResolver.AddS Each interface library that provide own requests is intended to provide method to register those requests in `SpeechletRequestResolver`. So do `AlexaSkillsKit.Interfaces.Display` in its `void AddDisplay(this SpeechletService service, IDisplaySpeechletAsync speechlet)` extention method and `AlexaSkillsKit.Interfaces.AudioPlayer` in `void AddAudioPlayer(this SpeechletService service, IAudioPlayerSpeechletAsync speechlet)`. -For more information on using external interfaces see [Use external interfaces](#Use external interfaces). -For more information on registering custom interfaces see [Implement external interface](#Implement external interface). +For more information on using external interfaces see [Use external interfaces](#Use%20external%20interfaces). +For more information on registering custom interfaces see [Implement external interface](#Implement%20external%20interface). ### Processing request From 9f18b3e6f3526b41e1d8b0a4c8732566853e7adc Mon Sep 17 00:00:00 2001 From: Sergey Greenko Date: Sun, 17 Dec 2017 23:29:31 +0200 Subject: [PATCH 08/16] fix readme links --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 99b0940..500c425 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Beyond the functionality in Amazon's AlexaSkillsKit for Java, AlexaSkillsKit.NET: * performs automatic session management so you can easily [build conversational Alexa apps](https://freebusy.io/blog/building-conversational-alexa-apps-for-amazon-echo) -* :new: can be extended to support custom and new coming interfaces (see [Implement external interface](#Implement%20external%20interface) section) +* :new: can be extended to support custom and new coming interfaces (see [Implement external interface](#implement-external-interface) section) This library was originally developed for and is in use at https://freebusy.io @@ -93,10 +93,10 @@ Request headers and body validation also takes place during this step. The `Spee Skill authors can set SpeechletService.ValidationHandler property to have better control on when exception should be thrown, or to throw custom exceptions instead. For backward compatibility, the `Speechlet` and `SpeechletAsync` helper abstract classes set `ValidationHandler` property of their internal service to virtual `OnRequestValidation` method. -Setting `ValidationHandler` property will override this behavior. See [Override request validation policy](#Override%20request%20validation%20policy) for more details on request validation. +Setting `ValidationHandler` property will override this behavior. See [Override request validation policy](#override-request-validation-policy) for more details on request validation. Request validation can be omitted by directly calling one of `SpeechletRequestEnvelope.FromJson` static methods. -`SpeechletRequestEnvelope` consists of `Version`, `Session`, `Context` and `Request` fields. See [Context](#Context) for more details on parsing context. +`SpeechletRequestEnvelope` consists of `Version`, `Session`, `Context` and `Request` fields. See [Context](#context-object) for more details on parsing context. Only version "1.0" of Alexa Skill API is supported. Otherwise `SpeechletValidationException` with `SpeechletRequestValidationResult.InvalidVersion` is thrown. Same is true, when calling `SpeechletRequestEnvelope.FromJson` methods directly. @@ -114,8 +114,8 @@ Both `Speechlet` and `SpeechletAsync` are calling `SpeechletRequestResolver.AddS Each interface library that provide own requests is intended to provide method to register those requests in `SpeechletRequestResolver`. So do `AlexaSkillsKit.Interfaces.Display` in its `void AddDisplay(this SpeechletService service, IDisplaySpeechletAsync speechlet)` extention method and `AlexaSkillsKit.Interfaces.AudioPlayer` in `void AddAudioPlayer(this SpeechletService service, IAudioPlayerSpeechletAsync speechlet)`. -For more information on using external interfaces see [Use external interfaces](#Use%20external%20interfaces). -For more information on registering custom interfaces see [Implement external interface](#Implement%20external%20interface). +For more information on using external interfaces see [Use external interfaces](#use-external-interfaces). +For more information on registering custom interfaces see [Implement external interface](#implement-external-interface). ### Processing request From 6b17303a6ec373984621e1316760845c31ffd17f Mon Sep 17 00:00:00 2001 From: Serhii Hrynko Date: Mon, 18 Dec 2017 10:58:28 +0200 Subject: [PATCH 09/16] Update nuget specific information, switch from project references to nuget package reference --- .nuget/AlexaSkillsKit.Lib.nuspec | 33 --------------- .nuget/pack.cmd | 2 - .../AlexaSkillsKit.Http.csproj | 26 +++++++----- ...exaSkillsKit.Interfaces.AudioPlayer.csproj | 29 +++++++++----- .../AlexaSkillsKit.Interfaces.Dialog.csproj | 29 +++++++++----- .../AlexaSkillsKit.Interfaces.Display.csproj | 29 +++++++++----- .../AlexaSkillsKit.Interfaces.VideoApp.csproj | 29 +++++++++----- AlexaSkillsKit.Lib/AlexaSkillsKit.Core.csproj | 26 ------------ AlexaSkillsKit.Lib/AlexaSkillsKit.csproj | 40 +++++++++++++++++++ .../AlexaSkillsKit.Sample.csproj | 4 +- .../AlexaSkillsKit.Tests.csproj | 4 +- AlexaSkillsKit.sln | 11 +---- README.md | 2 +- 13 files changed, 134 insertions(+), 130 deletions(-) delete mode 100644 .nuget/AlexaSkillsKit.Lib.nuspec delete mode 100644 .nuget/pack.cmd delete mode 100644 AlexaSkillsKit.Lib/AlexaSkillsKit.Core.csproj create mode 100644 AlexaSkillsKit.Lib/AlexaSkillsKit.csproj diff --git a/.nuget/AlexaSkillsKit.Lib.nuspec b/.nuget/AlexaSkillsKit.Lib.nuspec deleted file mode 100644 index 8249581..0000000 --- a/.nuget/AlexaSkillsKit.Lib.nuspec +++ /dev/null @@ -1,33 +0,0 @@ - - - - AlexaSkillsKit.NET - 1.5.2 - AlexaSkillsKit.NET - Stefan Negritoiu (FreeBusy) - Stefan Negritoiu (FreeBusy) - https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET - http://opensource.org/licenses/MIT - https://developer.amazon.com/public/binaries/content/gallery/developerportalpublic/solutions/alexa/dp_image_kit_02.png - false - -.NET library that simplifies Alexa skills development; same object model as Amazon's AlexaSkillsKit for Java - - -1.3.0: Incorporated Sept 2015 ASK update for account linking and access tokens -1.4.0: Ability to override request validation policy and support for SSML output speech type -1.5.0: Fully implement certificate verification requirement and support for Standard cards -1.5.2: reimplement request timestamp parsing to accept both ISO 8601 and Unix time formats - - Copyright 2017 Stefan Negritoiu (FreeBusy) and contributors - amazon echo alexa speechlet - - - - - - - - - - \ No newline at end of file diff --git a/.nuget/pack.cmd b/.nuget/pack.cmd deleted file mode 100644 index 0c350f8..0000000 --- a/.nuget/pack.cmd +++ /dev/null @@ -1,2 +0,0 @@ -msbuild /property:Configuration=Release ..\AlexaSkillsKit.Lib\AlexaSkillsKit.Lib.csproj -nuget pack AlexaSkillsKit.Lib.nuspec \ No newline at end of file diff --git a/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj b/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj index a4b601f..67151ac 100644 --- a/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj +++ b/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj @@ -2,16 +2,23 @@ netstandard1.1 - true - Stefan Negritoiu - Stefan Negritoiu AlexaSkillsKit - 1.5.0 - Copyright © 2015 Stefan Negritoiu (FreeBusy) - MIT + Copyright © 2018 Stefan Negritoiu (FreeBusy) and contributors + + Stefan Negritoiu (FreeBusy) + Stefan Negritoiu (FreeBusy) + 2.0.0 https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git git - Amazon Alexa Skill System.Net.Http + http://opensource.org/licenses/MIT + Amazon Alexa Skill Echo Show Speechlet System.Net.Http + true + https://developer.amazon.com/public/binaries/content/gallery/developerportalpublic/solutions/alexa/dp_image_kit_02.png + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET + System.Net.Http provider extentions for AlexaSkillsKit.Core + + 2.0.0: Separate Core and System.Net.Http Provider library. + @@ -19,11 +26,8 @@ + - - - - diff --git a/AlexaSkillsKit.Interfaces.AudioPlayer/AlexaSkillsKit.Interfaces.AudioPlayer.csproj b/AlexaSkillsKit.Interfaces.AudioPlayer/AlexaSkillsKit.Interfaces.AudioPlayer.csproj index 0a44c7d..af814b9 100644 --- a/AlexaSkillsKit.Interfaces.AudioPlayer/AlexaSkillsKit.Interfaces.AudioPlayer.csproj +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/AlexaSkillsKit.Interfaces.AudioPlayer.csproj @@ -2,20 +2,27 @@ netstandard1.1 - AlexaSkillsKit - Copyright © 2015 Stefan Negritoiu (FreeBusy) - - Stefan Negritoiu - 1.5.0 - https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git - git - MIT - Amazon Alexa Skill .Net - true + AlexaSkillsKit + Copyright © 2018 Stefan Negritoiu (FreeBusy) and contributors + + Stefan Negritoiu (FreeBusy) + Stefan Negritoiu (FreeBusy) + 2.0.0 + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git + git + http://opensource.org/licenses/MIT + Amazon Alexa Skill Echo Show Speechlet .Net AudioPlayer PlaybackController + true + https://developer.amazon.com/public/binaries/content/gallery/developerportalpublic/solutions/alexa/dp_image_kit_02.png + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET + AudioPlayer and PlaybackController interfaces support for AlexaSkillsKit + + 2.0.0: Extract AudioPlayer and PlaybackController interfaces to separate library + - + diff --git a/AlexaSkillsKit.Interfaces.Dialog/AlexaSkillsKit.Interfaces.Dialog.csproj b/AlexaSkillsKit.Interfaces.Dialog/AlexaSkillsKit.Interfaces.Dialog.csproj index 0a44c7d..d7e5f67 100644 --- a/AlexaSkillsKit.Interfaces.Dialog/AlexaSkillsKit.Interfaces.Dialog.csproj +++ b/AlexaSkillsKit.Interfaces.Dialog/AlexaSkillsKit.Interfaces.Dialog.csproj @@ -2,20 +2,27 @@ netstandard1.1 - AlexaSkillsKit - Copyright © 2015 Stefan Negritoiu (FreeBusy) - - Stefan Negritoiu - 1.5.0 - https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git - git - MIT - Amazon Alexa Skill .Net - true + AlexaSkillsKit + Copyright © 2018 Stefan Negritoiu (FreeBusy) and contributors + + Stefan Negritoiu (FreeBusy) + Stefan Negritoiu (FreeBusy) + 2.0.0 + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git + git + http://opensource.org/licenses/MIT + Amazon Alexa Skill Echo Show Speechlet .Net Dialog interface + true + https://developer.amazon.com/public/binaries/content/gallery/developerportalpublic/solutions/alexa/dp_image_kit_02.png + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET + Dialog interface support for AlexaSkillsKit + + 2.0.0: Extract Dialog interface to separate library + - + diff --git a/AlexaSkillsKit.Interfaces.Display/AlexaSkillsKit.Interfaces.Display.csproj b/AlexaSkillsKit.Interfaces.Display/AlexaSkillsKit.Interfaces.Display.csproj index 0a44c7d..6aa2ee3 100644 --- a/AlexaSkillsKit.Interfaces.Display/AlexaSkillsKit.Interfaces.Display.csproj +++ b/AlexaSkillsKit.Interfaces.Display/AlexaSkillsKit.Interfaces.Display.csproj @@ -2,20 +2,27 @@ netstandard1.1 - AlexaSkillsKit - Copyright © 2015 Stefan Negritoiu (FreeBusy) - - Stefan Negritoiu - 1.5.0 - https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git - git - MIT - Amazon Alexa Skill .Net - true + AlexaSkillsKit + Copyright © 2018 Stefan Negritoiu (FreeBusy) and contributors + + Stefan Negritoiu (FreeBusy) + Stefan Negritoiu (FreeBusy) + 2.0.0 + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git + git + http://opensource.org/licenses/MIT + Amazon Alexa Skill Echo Show Speechlet .Net Display interface + true + https://developer.amazon.com/public/binaries/content/gallery/developerportalpublic/solutions/alexa/dp_image_kit_02.png + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET + Display interface support for AlexaSkillsKit + + 2.0.0: Extract Display interface to separate library + - + diff --git a/AlexaSkillsKit.Interfaces.VideoApp/AlexaSkillsKit.Interfaces.VideoApp.csproj b/AlexaSkillsKit.Interfaces.VideoApp/AlexaSkillsKit.Interfaces.VideoApp.csproj index 0a44c7d..260b551 100644 --- a/AlexaSkillsKit.Interfaces.VideoApp/AlexaSkillsKit.Interfaces.VideoApp.csproj +++ b/AlexaSkillsKit.Interfaces.VideoApp/AlexaSkillsKit.Interfaces.VideoApp.csproj @@ -2,20 +2,27 @@ netstandard1.1 - AlexaSkillsKit - Copyright © 2015 Stefan Negritoiu (FreeBusy) - - Stefan Negritoiu - 1.5.0 - https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git - git - MIT - Amazon Alexa Skill .Net - true + AlexaSkillsKit + Copyright © 2018 Stefan Negritoiu (FreeBusy) and contributors + + Stefan Negritoiu (FreeBusy) + Stefan Negritoiu (FreeBusy) + 2.0.0 + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git + git + http://opensource.org/licenses/MIT + Amazon Alexa Skill Echo Show Speechlet .Net VideoApp interface + true + https://developer.amazon.com/public/binaries/content/gallery/developerportalpublic/solutions/alexa/dp_image_kit_02.png + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET + VideoApp interface support for AlexaSkillsKit + + 2.0.0: Extract VideoApp interface to separate library + - + diff --git a/AlexaSkillsKit.Lib/AlexaSkillsKit.Core.csproj b/AlexaSkillsKit.Lib/AlexaSkillsKit.Core.csproj deleted file mode 100644 index ca1c7c5..0000000 --- a/AlexaSkillsKit.Lib/AlexaSkillsKit.Core.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - netstandard1.1 - AlexaSkillsKit - Copyright © 2015 Stefan Negritoiu (FreeBusy) - - Stefan Negritoiu - 1.5.0 - https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git - git - MIT - Amazon Alexa Skill .Net - true - - - - - - - - - - - - diff --git a/AlexaSkillsKit.Lib/AlexaSkillsKit.csproj b/AlexaSkillsKit.Lib/AlexaSkillsKit.csproj new file mode 100644 index 0000000..eba9e2d --- /dev/null +++ b/AlexaSkillsKit.Lib/AlexaSkillsKit.csproj @@ -0,0 +1,40 @@ + + + + netstandard1.1 + AlexaSkillsKit + Copyright © 2018 Stefan Negritoiu (FreeBusy) and contributors + + Stefan Negritoiu (FreeBusy) + Stefan Negritoiu (FreeBusy) + 2.0.0 + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git + git + http://opensource.org/licenses/MIT + Amazon Alexa Skill Echo Show Speechlet .Net + true + https://developer.amazon.com/public/binaries/content/gallery/developerportalpublic/solutions/alexa/dp_image_kit_02.png + https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET + .NET library that simplifies Alexa skills development; same object model as Amazon's AlexaSkillsKit for Java + +1.3.0: Incorporated Sept 2015 ASK update for account linking and access tokens +1.4.0: Ability to override request validation policy and support for SSML output speech type +1.5.0: Fully implement certificate verification requirement and support for Standard cards +1.5.2: reimplement request timestamp parsing to accept both ISO 8601 and Unix time formats +1.6.0: Support external interfaces, .Net Standard support +2.0.0: Separate core, API Provider libraries and External interface libraries. Allow providing support for new interfaces and network APIs without intrusion into Core library. + + + + + + + + + + + + + + + diff --git a/AlexaSkillsKit.Sample/AlexaSkillsKit.Sample.csproj b/AlexaSkillsKit.Sample/AlexaSkillsKit.Sample.csproj index 794e7ea..131406d 100644 --- a/AlexaSkillsKit.Sample/AlexaSkillsKit.Sample.csproj +++ b/AlexaSkillsKit.Sample/AlexaSkillsKit.Sample.csproj @@ -327,9 +327,9 @@ {1517FE44-37AF-4585-AF54-0A20F222CFCB} AlexaSkillsKit.Interfaces.VideoApp - + {01b7fa9f-5f29-46fa-8e27-b731a2a95876} - AlexaSkillsKit.Core + AlexaSkillsKit diff --git a/AlexaSkillsKit.Tests/AlexaSkillsKit.Tests.csproj b/AlexaSkillsKit.Tests/AlexaSkillsKit.Tests.csproj index 29f57bf..9fa45e0 100644 --- a/AlexaSkillsKit.Tests/AlexaSkillsKit.Tests.csproj +++ b/AlexaSkillsKit.Tests/AlexaSkillsKit.Tests.csproj @@ -87,9 +87,9 @@ - + {01b7fa9f-5f29-46fa-8e27-b731a2a95876} - AlexaSkillsKit.Core + AlexaSkillsKit diff --git a/AlexaSkillsKit.sln b/AlexaSkillsKit.sln index a943d3e..77d31ea 100644 --- a/AlexaSkillsKit.sln +++ b/AlexaSkillsKit.sln @@ -1,22 +1,15 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27004.2010 +VisualStudioVersion = 15.0.27130.2003 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Sample", "AlexaSkillsKit.Sample\AlexaSkillsKit.Sample.csproj", "{9FDEF793-5832-4D37-B0D5-62C55AB1C12E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{D35AD75F-7926-41CB-8C8D-BB36370F3E76}" - ProjectSection(SolutionItems) = preProject - .nuget\AlexaSkillsKit.Lib.nuspec = .nuget\AlexaSkillsKit.Lib.nuspec - .nuget\NuGet.Config = .nuget\NuGet.Config - .nuget\NuGet.targets = .nuget\NuGet.targets - EndProjectSection -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Tests", "AlexaSkillsKit.Tests\AlexaSkillsKit.Tests.csproj", "{D0125A95-FD63-4A33-9220-206D4A1A14F0}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Http", "AlexaSkillsKit.Http\AlexaSkillsKit.Http.csproj", "{742331B9-5325-4F3F-B41F-EC41DFF9BD0C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AlexaSkillsKit.Core", "AlexaSkillsKit.Lib\AlexaSkillsKit.Core.csproj", "{01B7FA9F-5F29-46FA-8E27-B731A2A95876}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit", "AlexaSkillsKit.Lib\AlexaSkillsKit.csproj", "{01B7FA9F-5F29-46FA-8E27-B731A2A95876}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Interfaces.AudioPlayer", "AlexaSkillsKit.Interfaces.AudioPlayer\AlexaSkillsKit.Interfaces.AudioPlayer.csproj", "{2D25EE87-D293-4A62-BCEC-AACB4099ED9E}" EndProject diff --git a/README.md b/README.md index 500c425..1918860 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ This library was originally developed for and is in use at https://freebusy.io Library is currently available as a single NuGet package: https://www.nuget.org/packages/AlexaSkillsKit.Net/ Extensible version will be available as following NuGet packages: -* https://www.nuget.org/packages/AlexaSkillsKit.Core/ - core library +* https://www.nuget.org/packages/AlexaSkillsKit/ - core library * https://www.nuget.org/packages/AlexaSkillsKit.Http/ - System.Net.Http provider extentions * https://www.nuget.org/packages/AlexaSkillsKit.AspNetCore/ - ASP.Net Core Web API provider extentions * https://www.nuget.org/packages/AlexaSkillsKit.Interfaces.AudioPlayer/ - adds support of AudioPlayer and PlaybackController interfaces From 912824038fe87da4302a09192981f61cd91f4d59 Mon Sep 17 00:00:00 2001 From: Serhii Hrynko Date: Mon, 18 Dec 2017 16:50:14 +0200 Subject: [PATCH 10/16] Reverted to local project references. Reverted requests to AlexaSkillsKit.Speechlet namespace --- AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj | 5 ++++- .../AlexaSkillsKit.Interfaces.AudioPlayer.csproj | 2 +- AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerRequest.cs | 2 +- .../PlaybackControllerRequest.cs | 2 +- .../AlexaSkillsKit.Interfaces.Dialog.csproj | 2 +- .../AlexaSkillsKit.Interfaces.Display.csproj | 2 +- AlexaSkillsKit.Interfaces.Display/DisplayRequest.cs | 2 +- .../AlexaSkillsKit.Interfaces.VideoApp.csproj | 2 +- AlexaSkillsKit.Lib/Json/InterfaceResolver.cs | 2 +- AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs | 1 - AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs | 2 +- AlexaSkillsKit.Lib/Requests/ExtendedSpeechletRequest.cs | 2 +- AlexaSkillsKit.Lib/Requests/IntentRequest.cs | 4 ++-- AlexaSkillsKit.Lib/Requests/LaunchRequest.cs | 2 +- AlexaSkillsKit.Lib/Requests/SessionEndedRequest.cs | 3 +-- AlexaSkillsKit.Lib/Requests/SessionStartedRequest.cs | 2 +- AlexaSkillsKit.Lib/Requests/SpeechletRequest.cs | 2 +- .../Requests/SystemExceptionEncounteredRequest.cs | 5 ++--- AlexaSkillsKit.Lib/Speechlet/ISpeechlet.cs | 2 -- AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs | 1 - AlexaSkillsKit.Lib/Speechlet/ISystemSpeechlet.cs | 4 +--- AlexaSkillsKit.Lib/Speechlet/ISystemSpeechletAsync.cs | 3 +-- AlexaSkillsKit.Lib/Speechlet/Speechlet.cs | 3 --- AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs | 1 - AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs | 3 +-- AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs | 1 - AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs | 1 - AlexaSkillsKit.Lib/Speechlet/SystemSpeechletAsyncWrapper.cs | 3 +-- AlexaSkillsKit.Sample/Speechlet/SampleSessionSpeechlet.cs | 1 - 29 files changed, 26 insertions(+), 41 deletions(-) diff --git a/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj b/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj index 67151ac..1f45507 100644 --- a/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj +++ b/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj @@ -26,8 +26,11 @@ - + + + + diff --git a/AlexaSkillsKit.Interfaces.AudioPlayer/AlexaSkillsKit.Interfaces.AudioPlayer.csproj b/AlexaSkillsKit.Interfaces.AudioPlayer/AlexaSkillsKit.Interfaces.AudioPlayer.csproj index af814b9..1afb5be 100644 --- a/AlexaSkillsKit.Interfaces.AudioPlayer/AlexaSkillsKit.Interfaces.AudioPlayer.csproj +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/AlexaSkillsKit.Interfaces.AudioPlayer.csproj @@ -22,7 +22,7 @@ - + diff --git a/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerRequest.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerRequest.cs index 8f7218a..15bf253 100644 --- a/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerRequest.cs +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerRequest.cs @@ -1,4 +1,4 @@ -using AlexaSkillsKit.Requests; +using AlexaSkillsKit.Speechlet; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Interfaces.AudioPlayer diff --git a/AlexaSkillsKit.Interfaces.AudioPlayer/PlaybackControllerRequest.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/PlaybackControllerRequest.cs index 7e1e40f..5ebbcf8 100644 --- a/AlexaSkillsKit.Interfaces.AudioPlayer/PlaybackControllerRequest.cs +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/PlaybackControllerRequest.cs @@ -1,4 +1,4 @@ -using AlexaSkillsKit.Requests; +using AlexaSkillsKit.Speechlet; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Interfaces.AudioPlayer diff --git a/AlexaSkillsKit.Interfaces.Dialog/AlexaSkillsKit.Interfaces.Dialog.csproj b/AlexaSkillsKit.Interfaces.Dialog/AlexaSkillsKit.Interfaces.Dialog.csproj index d7e5f67..ddec3d4 100644 --- a/AlexaSkillsKit.Interfaces.Dialog/AlexaSkillsKit.Interfaces.Dialog.csproj +++ b/AlexaSkillsKit.Interfaces.Dialog/AlexaSkillsKit.Interfaces.Dialog.csproj @@ -22,7 +22,7 @@ - + diff --git a/AlexaSkillsKit.Interfaces.Display/AlexaSkillsKit.Interfaces.Display.csproj b/AlexaSkillsKit.Interfaces.Display/AlexaSkillsKit.Interfaces.Display.csproj index 6aa2ee3..3431a63 100644 --- a/AlexaSkillsKit.Interfaces.Display/AlexaSkillsKit.Interfaces.Display.csproj +++ b/AlexaSkillsKit.Interfaces.Display/AlexaSkillsKit.Interfaces.Display.csproj @@ -22,7 +22,7 @@ - + diff --git a/AlexaSkillsKit.Interfaces.Display/DisplayRequest.cs b/AlexaSkillsKit.Interfaces.Display/DisplayRequest.cs index bb4cf3c..bdd128e 100644 --- a/AlexaSkillsKit.Interfaces.Display/DisplayRequest.cs +++ b/AlexaSkillsKit.Interfaces.Display/DisplayRequest.cs @@ -1,4 +1,4 @@ -using AlexaSkillsKit.Requests; +using AlexaSkillsKit.Speechlet; using Newtonsoft.Json.Linq; namespace AlexaSkillsKit.Interfaces.Display diff --git a/AlexaSkillsKit.Interfaces.VideoApp/AlexaSkillsKit.Interfaces.VideoApp.csproj b/AlexaSkillsKit.Interfaces.VideoApp/AlexaSkillsKit.Interfaces.VideoApp.csproj index 260b551..6392cea 100644 --- a/AlexaSkillsKit.Interfaces.VideoApp/AlexaSkillsKit.Interfaces.VideoApp.csproj +++ b/AlexaSkillsKit.Interfaces.VideoApp/AlexaSkillsKit.Interfaces.VideoApp.csproj @@ -22,7 +22,7 @@ - + diff --git a/AlexaSkillsKit.Lib/Json/InterfaceResolver.cs b/AlexaSkillsKit.Lib/Json/InterfaceResolver.cs index 9ef2f3a..dc253ad 100644 --- a/AlexaSkillsKit.Lib/Json/InterfaceResolver.cs +++ b/AlexaSkillsKit.Lib/Json/InterfaceResolver.cs @@ -1,4 +1,4 @@ -using AlexaSkillsKit.Requests; +using AlexaSkillsKit.Speechlet; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; diff --git a/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs b/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs index 3f16b54..d5aa185 100644 --- a/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs +++ b/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs @@ -4,7 +4,6 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using AlexaSkillsKit.Speechlet; -using AlexaSkillsKit.Requests; using AlexaSkillsKit.Authentication; namespace AlexaSkillsKit.Json diff --git a/AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs b/AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs index 682193f..7d22730 100644 --- a/AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs +++ b/AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs @@ -1,4 +1,4 @@ -using AlexaSkillsKit.Requests; +using AlexaSkillsKit.Speechlet; using Newtonsoft.Json.Linq; using System.Collections.Generic; diff --git a/AlexaSkillsKit.Lib/Requests/ExtendedSpeechletRequest.cs b/AlexaSkillsKit.Lib/Requests/ExtendedSpeechletRequest.cs index f2297d3..915e250 100644 --- a/AlexaSkillsKit.Lib/Requests/ExtendedSpeechletRequest.cs +++ b/AlexaSkillsKit.Lib/Requests/ExtendedSpeechletRequest.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json.Linq; -namespace AlexaSkillsKit.Requests +namespace AlexaSkillsKit.Speechlet { /// /// https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#request-body-parameters diff --git a/AlexaSkillsKit.Lib/Requests/IntentRequest.cs b/AlexaSkillsKit.Lib/Requests/IntentRequest.cs index 18a4919..37f639e 100644 --- a/AlexaSkillsKit.Lib/Requests/IntentRequest.cs +++ b/AlexaSkillsKit.Lib/Requests/IntentRequest.cs @@ -1,10 +1,10 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using System; using AlexaSkillsKit.Slu; using Newtonsoft.Json.Linq; +using System; -namespace AlexaSkillsKit.Requests +namespace AlexaSkillsKit.Speechlet { public class IntentRequest : SpeechletRequest { diff --git a/AlexaSkillsKit.Lib/Requests/LaunchRequest.cs b/AlexaSkillsKit.Lib/Requests/LaunchRequest.cs index 1e3bf57..007bfa7 100644 --- a/AlexaSkillsKit.Lib/Requests/LaunchRequest.cs +++ b/AlexaSkillsKit.Lib/Requests/LaunchRequest.cs @@ -2,7 +2,7 @@ using Newtonsoft.Json.Linq; -namespace AlexaSkillsKit.Requests +namespace AlexaSkillsKit.Speechlet { public class LaunchRequest : SpeechletRequest { diff --git a/AlexaSkillsKit.Lib/Requests/SessionEndedRequest.cs b/AlexaSkillsKit.Lib/Requests/SessionEndedRequest.cs index 3712950..cb407d1 100644 --- a/AlexaSkillsKit.Lib/Requests/SessionEndedRequest.cs +++ b/AlexaSkillsKit.Lib/Requests/SessionEndedRequest.cs @@ -1,10 +1,9 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using AlexaSkillsKit.Speechlet; using Newtonsoft.Json.Linq; using System; -namespace AlexaSkillsKit.Requests +namespace AlexaSkillsKit.Speechlet { public class SessionEndedRequest : SpeechletRequest { diff --git a/AlexaSkillsKit.Lib/Requests/SessionStartedRequest.cs b/AlexaSkillsKit.Lib/Requests/SessionStartedRequest.cs index 92a68be..5b8fdf1 100644 --- a/AlexaSkillsKit.Lib/Requests/SessionStartedRequest.cs +++ b/AlexaSkillsKit.Lib/Requests/SessionStartedRequest.cs @@ -1,6 +1,6 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -namespace AlexaSkillsKit.Requests +namespace AlexaSkillsKit.Speechlet { public class SessionStartedRequest : SpeechletRequest { diff --git a/AlexaSkillsKit.Lib/Requests/SpeechletRequest.cs b/AlexaSkillsKit.Lib/Requests/SpeechletRequest.cs index 041efaf..0e91842 100644 --- a/AlexaSkillsKit.Lib/Requests/SpeechletRequest.cs +++ b/AlexaSkillsKit.Lib/Requests/SpeechletRequest.cs @@ -6,7 +6,7 @@ using System; using System.Linq; -namespace AlexaSkillsKit.Requests +namespace AlexaSkillsKit.Speechlet { public abstract class SpeechletRequest { diff --git a/AlexaSkillsKit.Lib/Requests/SystemExceptionEncounteredRequest.cs b/AlexaSkillsKit.Lib/Requests/SystemExceptionEncounteredRequest.cs index 5c85a0a..247959d 100644 --- a/AlexaSkillsKit.Lib/Requests/SystemExceptionEncounteredRequest.cs +++ b/AlexaSkillsKit.Lib/Requests/SystemExceptionEncounteredRequest.cs @@ -1,7 +1,6 @@ -using AlexaSkillsKit.Speechlet; -using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Linq; -namespace AlexaSkillsKit.Requests +namespace AlexaSkillsKit.Speechlet { /// /// https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#system-exceptionencountered diff --git a/AlexaSkillsKit.Lib/Speechlet/ISpeechlet.cs b/AlexaSkillsKit.Lib/Speechlet/ISpeechlet.cs index ab96d41..4410b46 100644 --- a/AlexaSkillsKit.Lib/Speechlet/ISpeechlet.cs +++ b/AlexaSkillsKit.Lib/Speechlet/ISpeechlet.cs @@ -1,7 +1,5 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using AlexaSkillsKit.Requests; - namespace AlexaSkillsKit.Speechlet { public interface ISpeechlet diff --git a/AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs b/AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs index cebbd39..328fbef 100644 --- a/AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs +++ b/AlexaSkillsKit.Lib/Speechlet/ISpeechletAsync.cs @@ -1,6 +1,5 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using AlexaSkillsKit.Requests; using System.Threading.Tasks; namespace AlexaSkillsKit.Speechlet diff --git a/AlexaSkillsKit.Lib/Speechlet/ISystemSpeechlet.cs b/AlexaSkillsKit.Lib/Speechlet/ISystemSpeechlet.cs index 21154b4..3f1d1e5 100644 --- a/AlexaSkillsKit.Lib/Speechlet/ISystemSpeechlet.cs +++ b/AlexaSkillsKit.Lib/Speechlet/ISystemSpeechlet.cs @@ -1,6 +1,4 @@ -using AlexaSkillsKit.Requests; - -namespace AlexaSkillsKit.Speechlet +namespace AlexaSkillsKit.Speechlet { public interface ISystemSpeechlet { diff --git a/AlexaSkillsKit.Lib/Speechlet/ISystemSpeechletAsync.cs b/AlexaSkillsKit.Lib/Speechlet/ISystemSpeechletAsync.cs index 9c04b11..d458c06 100644 --- a/AlexaSkillsKit.Lib/Speechlet/ISystemSpeechletAsync.cs +++ b/AlexaSkillsKit.Lib/Speechlet/ISystemSpeechletAsync.cs @@ -1,5 +1,4 @@ -using AlexaSkillsKit.Requests; -using System.Threading.Tasks; +using System.Threading.Tasks; namespace AlexaSkillsKit.Speechlet { diff --git a/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs b/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs index 7045817..21a8b16 100644 --- a/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs +++ b/AlexaSkillsKit.Lib/Speechlet/Speechlet.cs @@ -1,8 +1,5 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using AlexaSkillsKit.Requests; -using System; - namespace AlexaSkillsKit.Speechlet { public abstract class Speechlet : SpeechletBase, ISpeechlet diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs index 71ec95e..86bc7d6 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsync.cs @@ -1,6 +1,5 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using AlexaSkillsKit.Requests; using System.Threading.Tasks; namespace AlexaSkillsKit.Speechlet diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs index b962b67..a9390d3 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletAsyncWrapper.cs @@ -1,5 +1,4 @@ -using AlexaSkillsKit.Requests; -using System.Threading.Tasks; +using System.Threading.Tasks; namespace AlexaSkillsKit.Speechlet { diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs index 71e3074..fd81bfe 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs @@ -1,6 +1,5 @@ using AlexaSkillsKit.Authentication; using AlexaSkillsKit.Json; -using AlexaSkillsKit.Requests; using Newtonsoft.Json; using System; using System.Collections.Generic; diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs index c4f77f0..77f2a5b 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs @@ -1,5 +1,4 @@ using AlexaSkillsKit.Json; -using AlexaSkillsKit.Requests; namespace AlexaSkillsKit.Speechlet { diff --git a/AlexaSkillsKit.Lib/Speechlet/SystemSpeechletAsyncWrapper.cs b/AlexaSkillsKit.Lib/Speechlet/SystemSpeechletAsyncWrapper.cs index d8d1584..5d0291a 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SystemSpeechletAsyncWrapper.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SystemSpeechletAsyncWrapper.cs @@ -1,5 +1,4 @@ -using AlexaSkillsKit.Requests; -using System.Threading.Tasks; +using System.Threading.Tasks; namespace AlexaSkillsKit.Speechlet { diff --git a/AlexaSkillsKit.Sample/Speechlet/SampleSessionSpeechlet.cs b/AlexaSkillsKit.Sample/Speechlet/SampleSessionSpeechlet.cs index 30ed7f1..f753be9 100644 --- a/AlexaSkillsKit.Sample/Speechlet/SampleSessionSpeechlet.cs +++ b/AlexaSkillsKit.Sample/Speechlet/SampleSessionSpeechlet.cs @@ -4,7 +4,6 @@ using AlexaSkillsKit.Interfaces.Display.Directives; using AlexaSkillsKit.Interfaces.VideoApp; using AlexaSkillsKit.Interfaces.VideoApp.Directives; -using AlexaSkillsKit.Requests; using AlexaSkillsKit.Slu; using AlexaSkillsKit.Speechlet; using AlexaSkillsKit.UI; From 8f93e95e007e33dcb6689fed8952aa71241a24c5 Mon Sep 17 00:00:00 2001 From: Serhii Hrynko Date: Mon, 18 Dec 2017 17:25:38 +0200 Subject: [PATCH 11/16] Request resolver made non-static --- .../AudioPlayerSpeechletServiceExtensions.cs | 4 +-- .../DisplaySpeechletServiceExtensions.cs | 2 +- .../Json/SpeechletRequestEnvelope.cs | 8 ++--- .../Json/SpeechletRequestResolver.cs | 30 +++++++++++++++---- .../Requests/SpeechletRequest.cs | 18 ----------- AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs | 22 ++++++++++++-- .../Speechlet/SpeechletService.cs | 4 ++- .../Speechlet/SpeechletServiceExtensions.cs | 4 +-- .../Helpers/DateTimeHelpersTests.cs | 12 ++++++-- 9 files changed, 65 insertions(+), 39 deletions(-) diff --git a/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerSpeechletServiceExtensions.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerSpeechletServiceExtensions.cs index e650b34..c1eedf4 100644 --- a/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerSpeechletServiceExtensions.cs +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerSpeechletServiceExtensions.cs @@ -15,14 +15,14 @@ public static void AddAudioPlayer(this SpeechletService service, IAudioPlayerSpe var audioPlayerResolver = new InterfaceResolver() .WithDefaultDeserializer((json, subtype) => new AudioPlayerRequest(json, subtype)) .WithDeserializer("PlaybackFailed", (json, subtype) => new AudioPlayerPlaybackFailedRequest(json, subtype)); - SpeechletRequestResolver.AddInterface("AudioPlayer", audioPlayerResolver); + service.RequestResolver.AddInterface("AudioPlayer", audioPlayerResolver); service.AddHandler( async (request, context) => await speechlet.OnAudioPlayerAsync(request, context)); var playbackControllerResolver = new InterfaceResolver() .WithDefaultDeserializer((json, subtype) => new PlaybackControllerRequest(json, subtype)); - SpeechletRequestResolver.AddInterface("PlaybackController", playbackControllerResolver); + service.RequestResolver.AddInterface("PlaybackController", playbackControllerResolver); service.AddHandler( async (request, context) => await speechlet.OnPlaybackControllerAsync(request, context)); diff --git a/AlexaSkillsKit.Interfaces.Display/DisplaySpeechletServiceExtensions.cs b/AlexaSkillsKit.Interfaces.Display/DisplaySpeechletServiceExtensions.cs index b08296b..a13a534 100644 --- a/AlexaSkillsKit.Interfaces.Display/DisplaySpeechletServiceExtensions.cs +++ b/AlexaSkillsKit.Interfaces.Display/DisplaySpeechletServiceExtensions.cs @@ -14,7 +14,7 @@ public static void AddDisplay(this SpeechletService service, IDisplaySpeechletAs var displayResolver = new InterfaceResolver() .WithDefaultDeserializer((json, subtype) => new DisplayRequest(json, subtype)); - SpeechletRequestResolver.AddInterface("Display", displayResolver); + service.RequestResolver.AddInterface("Display", displayResolver); service.AddHandler(async (request, context) => await speechlet.OnDisplayAsync(request, context)); } diff --git a/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs b/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs index d5aa185..b687bb7 100644 --- a/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs +++ b/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs @@ -15,23 +15,23 @@ public class SpeechletRequestEnvelope /// /// /// - public static SpeechletRequestEnvelope FromJson(string content) { + public static SpeechletRequestEnvelope FromJson(SpeechletRequestResolver resolver, string content) { if (String.IsNullOrEmpty(content)) { throw new SpeechletValidationException(SpeechletRequestValidationResult.NoContent, "Request content is empty"); } JObject json = JsonConvert.DeserializeObject(content, Sdk.DeserializationSettings); - return FromJson(json); + return FromJson(resolver, json); } - public static SpeechletRequestEnvelope FromJson(JObject json) { + public static SpeechletRequestEnvelope FromJson(SpeechletRequestResolver resolver, JObject json) { var version = json.Value("version"); if (version != null && version != Sdk.VERSION) { throw new SpeechletValidationException(SpeechletRequestValidationResult.InvalidVersion, "Request must conform to 1.0 schema."); } return new SpeechletRequestEnvelope { - Request = SpeechletRequest.FromJson(json.Value("request")), + Request = resolver.FromJson(json.Value("request")), Session = Session.FromJson(json.Value("session")), Version = version, Context = Context.FromJson(json.Value("context")) diff --git a/AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs b/AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs index 7d22730..b5087d9 100644 --- a/AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs +++ b/AlexaSkillsKit.Lib/Json/SpeechletRequestResolver.cs @@ -1,25 +1,43 @@ using AlexaSkillsKit.Speechlet; using Newtonsoft.Json.Linq; +using System; using System.Collections.Generic; +using System.Linq; namespace AlexaSkillsKit.Json { - public static class SpeechletRequestResolver + public class SpeechletRequestResolver { - private static IDictionary resolvers + private IDictionary resolvers = new Dictionary(); - public static SpeechletRequest FromJson(string type, string subtype, JObject json) { + private SpeechletRequest FromJson(string type, string subtype, JObject json) { if (json == null || !resolvers.ContainsKey(type)) return null; return resolvers[type].FromJson(subtype, json); } - public static void AddInterface(string name, InterfaceResolver resolver) { + public SpeechletRequest FromJson(JObject json) { + var requestTypeParts = json?.Value("type")?.Split('.'); + if (requestTypeParts == null) { + throw new ArgumentException("json"); + } + + var requestType = requestTypeParts.Length > 1 ? requestTypeParts[0] : string.Empty; + var requestSubtype = requestTypeParts.Last(); + var request = FromJson(requestType, requestSubtype, json); + if (request == null) { + throw new ArgumentException("json"); + } + + return request; + } + + public void AddInterface(string name, InterfaceResolver resolver) { resolvers[name] = resolver; } - public static void AddStandard() { + public void AddStandard() { var standardResolver = new InterfaceResolver() .WithDeserializer(nameof(LaunchRequest), (json, subtype) => new LaunchRequest(json)) .WithDeserializer(nameof(IntentRequest), (json, subtype) => new IntentRequest(json)) @@ -27,7 +45,7 @@ public static void AddStandard() { AddInterface(string.Empty, standardResolver); } - public static void AddSystem() { + public void AddSystem() { var systemResolver = new InterfaceResolver() .WithDeserializer("ExceptionEncountered", (json, subtype) => new SystemExceptionEncounteredRequest(json, subtype)); AddInterface("System", systemResolver); diff --git a/AlexaSkillsKit.Lib/Requests/SpeechletRequest.cs b/AlexaSkillsKit.Lib/Requests/SpeechletRequest.cs index 0e91842..64b7a2c 100644 --- a/AlexaSkillsKit.Lib/Requests/SpeechletRequest.cs +++ b/AlexaSkillsKit.Lib/Requests/SpeechletRequest.cs @@ -1,31 +1,13 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. using AlexaSkillsKit.Helpers; -using AlexaSkillsKit.Json; using Newtonsoft.Json.Linq; using System; -using System.Linq; namespace AlexaSkillsKit.Speechlet { public abstract class SpeechletRequest { - public static SpeechletRequest FromJson(JObject json) { - var requestTypeParts = json?.Value("type")?.Split('.'); - if (requestTypeParts == null) { - throw new ArgumentException("json"); - } - - var requestType = requestTypeParts.Length > 1 ? requestTypeParts[0] : string.Empty; - var requestSubtype = requestTypeParts.Last(); - var request = SpeechletRequestResolver.FromJson(requestType, requestSubtype, json); - if (request == null) { - throw new ArgumentException("json"); - } - - return request; - } - protected SpeechletRequest(JObject json) { RequestId = json.Value("requestId"); Timestamp = DateTimeHelpers.FromAlexaTimestamp(json); diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs index 19aca48..66d0aad 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs @@ -4,6 +4,7 @@ using AlexaSkillsKit.Json; using Newtonsoft.Json.Linq; using System; +using System.Threading.Tasks; namespace AlexaSkillsKit.Speechlet { @@ -22,7 +23,7 @@ public SpeechletBase() { /// /// public string ProcessRequest(string requestContent) { - var request = SpeechletRequestEnvelope.FromJson(requestContent); + var request = SpeechletRequestEnvelope.FromJson(Service.RequestResolver, requestContent); return AsyncHelpers.RunSync(async () => (await Service.ProcessRequestAsync(request))?.ToJson()); } @@ -33,11 +34,28 @@ public string ProcessRequest(string requestContent) { /// /// public string ProcessRequest(JObject requestJson) { - var request = SpeechletRequestEnvelope.FromJson(requestJson); + var request = SpeechletRequestEnvelope.FromJson(Service.RequestResolver, requestJson); return AsyncHelpers.RunSync(async () => (await Service.ProcessRequestAsync(request))?.ToJson()); } + public async Task ProcessRequestAsync(string requestContent) { + var request = SpeechletRequestEnvelope.FromJson(Service.RequestResolver, requestContent); + return (await Service.ProcessRequestAsync(request))?.ToJson(); + } + + + /// + /// + /// + /// + /// + public async Task ProcessRequestAsync(JObject requestJson) { + var request = SpeechletRequestEnvelope.FromJson(Service.RequestResolver, requestJson); + return (await Service.ProcessRequestAsync(request))?.ToJson(); + } + + /// /// Opportunity to set policy for handling requests with invalid signatures and/or timestamps /// diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs index fd81bfe..3a2572c 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs @@ -16,6 +16,8 @@ private IDictionary ValidationHandler { get; set; } + public SpeechletRequestResolver RequestResolver { get; set; } + public void AddHandler(Func> handler) where T : SpeechletRequest { handlers[typeof(T)] = async (request, session, context) => await handler(request as T, session, context); } @@ -46,7 +48,7 @@ public async Task GetRequestAsync(string content, stri SpeechletRequestEnvelope alexaRequest = null; try { - alexaRequest = SpeechletRequestEnvelope.FromJson(content); + alexaRequest = SpeechletRequestEnvelope.FromJson(RequestResolver, content); } catch (SpeechletValidationException ex) { validationResult |= ex.ValidationResult; diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs index 77f2a5b..1714d0c 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs @@ -16,7 +16,7 @@ public static void AddStandard(this SpeechletService service, ISpeechletAsync sp return null; }); - SpeechletRequestResolver.AddStandard(); + service.RequestResolver.AddStandard(); } public static void AddStandard(this SpeechletService service, ISpeechlet speechlet) { @@ -29,7 +29,7 @@ public static void AddSystem(this SpeechletService service, ISystemSpeechletAsyn return null; }); - SpeechletRequestResolver.AddSystem(); + service.RequestResolver.AddSystem(); } public static void AddSystem(this SpeechletService service, ISystemSpeechlet speechlet) { diff --git a/AlexaSkillsKit.Tests/Helpers/DateTimeHelpersTests.cs b/AlexaSkillsKit.Tests/Helpers/DateTimeHelpersTests.cs index 7ba5ffe..91ea0dc 100644 --- a/AlexaSkillsKit.Tests/Helpers/DateTimeHelpersTests.cs +++ b/AlexaSkillsKit.Tests/Helpers/DateTimeHelpersTests.cs @@ -14,10 +14,12 @@ public void RequestWithIso8601TimestampTest() { SpeechletRequestValidationResult validationResult = SpeechletRequestValidationResult.OK; SpeechletRequestEnvelope alexaRequest = null; + var resolver = new SpeechletRequestResolver(); + resolver.AddStandard(); var alexaContent = File.ReadAllText(TestDataFile); try { - alexaRequest = SpeechletRequestEnvelope.FromJson(alexaContent); + alexaRequest = SpeechletRequestEnvelope.FromJson(resolver, alexaContent); } catch (Exception ex) when (ex is Newtonsoft.Json.JsonReaderException || ex is InvalidCastException || ex is FormatException) { @@ -34,10 +36,12 @@ public void RequestWithUnixTimeTimestampTest() { SpeechletRequestValidationResult validationResult = SpeechletRequestValidationResult.OK; SpeechletRequestEnvelope alexaRequest = null; + var resolver = new SpeechletRequestResolver(); + resolver.AddStandard(); var alexaContent = File.ReadAllText(TestDataFile); try { - alexaRequest = SpeechletRequestEnvelope.FromJson(alexaContent); + alexaRequest = SpeechletRequestEnvelope.FromJson(resolver, alexaContent); } catch (Exception ex) when (ex is Newtonsoft.Json.JsonReaderException || ex is InvalidCastException || ex is FormatException) { @@ -54,10 +58,12 @@ public void RequestWithInvalidTimestampTest(){ SpeechletRequestValidationResult validationResult = SpeechletRequestValidationResult.OK; SpeechletRequestEnvelope alexaRequest = null; + var resolver = new SpeechletRequestResolver(); + resolver.AddStandard(); var alexaContent = File.ReadAllText(TestDataFile); try { - alexaRequest = SpeechletRequestEnvelope.FromJson(alexaContent); + alexaRequest = SpeechletRequestEnvelope.FromJson(resolver, alexaContent); } catch (Exception ex) when (ex is Newtonsoft.Json.JsonReaderException || ex is InvalidCastException || ex is FormatException) { From 7f01626ca080cd44ace37010709d66d6ec3aea87 Mon Sep 17 00:00:00 2001 From: Sergey Greenko Date: Mon, 18 Dec 2017 22:29:21 +0200 Subject: [PATCH 12/16] NuGet Package referrence combined with local project references --- .../AlexaSkillsKit.Http.csproj | 1 + ...exaSkillsKit.Interfaces.AudioPlayer.csproj | 4 ++++ .../AlexaSkillsKit.Interfaces.Dialog.csproj | 4 ++++ .../AlexaSkillsKit.Interfaces.Display.csproj | 4 ++++ .../AlexaSkillsKit.Interfaces.VideoApp.csproj | 4 ++++ .../SpeechletRequestResolverExtensions.cs | 21 +++++++++++++++++++ .../Speechlet/SpeechletService.cs | 2 +- 7 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 AlexaSkillsKit.Lib/Json/SpeechletRequestResolverExtensions.cs diff --git a/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj b/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj index 1f45507..323058d 100644 --- a/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj +++ b/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj @@ -26,6 +26,7 @@ + diff --git a/AlexaSkillsKit.Interfaces.AudioPlayer/AlexaSkillsKit.Interfaces.AudioPlayer.csproj b/AlexaSkillsKit.Interfaces.AudioPlayer/AlexaSkillsKit.Interfaces.AudioPlayer.csproj index 1afb5be..77aad13 100644 --- a/AlexaSkillsKit.Interfaces.AudioPlayer/AlexaSkillsKit.Interfaces.AudioPlayer.csproj +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/AlexaSkillsKit.Interfaces.AudioPlayer.csproj @@ -21,6 +21,10 @@ + + + + diff --git a/AlexaSkillsKit.Interfaces.Dialog/AlexaSkillsKit.Interfaces.Dialog.csproj b/AlexaSkillsKit.Interfaces.Dialog/AlexaSkillsKit.Interfaces.Dialog.csproj index ddec3d4..e1f0188 100644 --- a/AlexaSkillsKit.Interfaces.Dialog/AlexaSkillsKit.Interfaces.Dialog.csproj +++ b/AlexaSkillsKit.Interfaces.Dialog/AlexaSkillsKit.Interfaces.Dialog.csproj @@ -21,6 +21,10 @@ + + + + diff --git a/AlexaSkillsKit.Interfaces.Display/AlexaSkillsKit.Interfaces.Display.csproj b/AlexaSkillsKit.Interfaces.Display/AlexaSkillsKit.Interfaces.Display.csproj index 3431a63..61d3b24 100644 --- a/AlexaSkillsKit.Interfaces.Display/AlexaSkillsKit.Interfaces.Display.csproj +++ b/AlexaSkillsKit.Interfaces.Display/AlexaSkillsKit.Interfaces.Display.csproj @@ -21,6 +21,10 @@ + + + + diff --git a/AlexaSkillsKit.Interfaces.VideoApp/AlexaSkillsKit.Interfaces.VideoApp.csproj b/AlexaSkillsKit.Interfaces.VideoApp/AlexaSkillsKit.Interfaces.VideoApp.csproj index 6392cea..7d9f4ac 100644 --- a/AlexaSkillsKit.Interfaces.VideoApp/AlexaSkillsKit.Interfaces.VideoApp.csproj +++ b/AlexaSkillsKit.Interfaces.VideoApp/AlexaSkillsKit.Interfaces.VideoApp.csproj @@ -21,6 +21,10 @@ + + + + diff --git a/AlexaSkillsKit.Lib/Json/SpeechletRequestResolverExtensions.cs b/AlexaSkillsKit.Lib/Json/SpeechletRequestResolverExtensions.cs new file mode 100644 index 0000000..0f15a86 --- /dev/null +++ b/AlexaSkillsKit.Lib/Json/SpeechletRequestResolverExtensions.cs @@ -0,0 +1,21 @@ +using AlexaSkillsKit.Speechlet; + +namespace AlexaSkillsKit.Json +{ + public static class SpeechletRequestResolverExtensions + { + public static void AddStandard(this SpeechletRequestResolver resolver) { + var standardResolver = new InterfaceResolver() + .WithDeserializer(nameof(LaunchRequest), (json, subtype) => new LaunchRequest(json)) + .WithDeserializer(nameof(IntentRequest), (json, subtype) => new IntentRequest(json)) + .WithDeserializer(nameof(SessionEndedRequest), (json, subtype) => new SessionEndedRequest(json)); + resolver.AddInterface(string.Empty, standardResolver); + } + + public static void AddSystem(this SpeechletRequestResolver resolver) { + var systemResolver = new InterfaceResolver() + .WithDeserializer("ExceptionEncountered", (json, subtype) => new SystemExceptionEncounteredRequest(json, subtype)); + resolver.AddInterface("System", systemResolver); + } + } +} \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs index 3a2572c..e174dd2 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs @@ -16,7 +16,7 @@ private IDictionary ValidationHandler { get; set; } - public SpeechletRequestResolver RequestResolver { get; set; } + public SpeechletRequestResolver RequestResolver { get; } = new SpeechletRequestResolver(); public void AddHandler(Func> handler) where T : SpeechletRequest { handlers[typeof(T)] = async (request, session, context) => await handler(request as T, session, context); From 8ae31786d21a276746b9e6bb5bd63eced213883f Mon Sep 17 00:00:00 2001 From: Sergey Greenko Date: Mon, 18 Dec 2017 23:24:52 +0200 Subject: [PATCH 13/16] .Net StandardLibrary contains System.Net.Http anyway --- .../AlexaSkillsKit.Http.csproj | 37 ------------------- .../Speechlet/SystemNetHttpExtensions.cs | 5 +-- .../AlexaSkillsKit.Sample.csproj | 4 -- .../Speechlet/AlexaController.cs | 3 +- AlexaSkillsKit.sln | 18 +++------ 5 files changed, 9 insertions(+), 58 deletions(-) delete mode 100644 AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj rename AlexaSkillsKit.Http/SpeechletServiceExtensions.cs => AlexaSkillsKit.Lib/Speechlet/SystemNetHttpExtensions.cs (95%) diff --git a/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj b/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj deleted file mode 100644 index 323058d..0000000 --- a/AlexaSkillsKit.Http/AlexaSkillsKit.Http.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - netstandard1.1 - AlexaSkillsKit - Copyright © 2018 Stefan Negritoiu (FreeBusy) and contributors - - Stefan Negritoiu (FreeBusy) - Stefan Negritoiu (FreeBusy) - 2.0.0 - https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET.git - git - http://opensource.org/licenses/MIT - Amazon Alexa Skill Echo Show Speechlet System.Net.Http - true - https://developer.amazon.com/public/binaries/content/gallery/developerportalpublic/solutions/alexa/dp_image_kit_02.png - https://github.com/AreYouFreeBusy/AlexaSkillsKit.NET - System.Net.Http provider extentions for AlexaSkillsKit.Core - - 2.0.0: Separate Core and System.Net.Http Provider library. - - - - - AnyCPU - - - - - - - - - - - - diff --git a/AlexaSkillsKit.Http/SpeechletServiceExtensions.cs b/AlexaSkillsKit.Lib/Speechlet/SystemNetHttpExtensions.cs similarity index 95% rename from AlexaSkillsKit.Http/SpeechletServiceExtensions.cs rename to AlexaSkillsKit.Lib/Speechlet/SystemNetHttpExtensions.cs index 85270ca..d1d00dd 100644 --- a/AlexaSkillsKit.Http/SpeechletServiceExtensions.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SystemNetHttpExtensions.cs @@ -1,15 +1,14 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using AlexaSkillsKit.Speechlet; using System.Linq; using System.Net; using System.Net.Http; using System.Text; using System.Threading.Tasks; -namespace AlexaSkillsKit.Http +namespace AlexaSkillsKit.Speechlet { - public static class SpeechletServiceExtensions + public static class SystemNetHttpExtensions { /// /// Processes Alexa request AND validates request signature diff --git a/AlexaSkillsKit.Sample/AlexaSkillsKit.Sample.csproj b/AlexaSkillsKit.Sample/AlexaSkillsKit.Sample.csproj index 131406d..7ab70d9 100644 --- a/AlexaSkillsKit.Sample/AlexaSkillsKit.Sample.csproj +++ b/AlexaSkillsKit.Sample/AlexaSkillsKit.Sample.csproj @@ -315,10 +315,6 @@ - - {742331b9-5325-4f3f-b41f-ec41dff9bd0c} - AlexaSkillsKit.Http - {6224BE94-2A4B-4AB2-BA8A-4BD3DF47FC4F} AlexaSkillsKit.Interfaces.Display diff --git a/AlexaSkillsKit.Sample/Speechlet/AlexaController.cs b/AlexaSkillsKit.Sample/Speechlet/AlexaController.cs index d833a8c..e734702 100644 --- a/AlexaSkillsKit.Sample/Speechlet/AlexaController.cs +++ b/AlexaSkillsKit.Sample/Speechlet/AlexaController.cs @@ -1,7 +1,6 @@ // Copyright 2015 Stefan Negritoiu (FreeBusy). See LICENSE file for more information. -using AlexaSkillsKit.Http; -using AlexaSkillsKit.Interfaces.Display; +using AlexaSkillsKit.Speechlet; using System.Net.Http; using System.Web.Http; diff --git a/AlexaSkillsKit.sln b/AlexaSkillsKit.sln index 77d31ea..bc8fe4f 100644 --- a/AlexaSkillsKit.sln +++ b/AlexaSkillsKit.sln @@ -1,23 +1,21 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27130.2003 +VisualStudioVersion = 15.0.27004.2010 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Sample", "AlexaSkillsKit.Sample\AlexaSkillsKit.Sample.csproj", "{9FDEF793-5832-4D37-B0D5-62C55AB1C12E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Tests", "AlexaSkillsKit.Tests\AlexaSkillsKit.Tests.csproj", "{D0125A95-FD63-4A33-9220-206D4A1A14F0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Http", "AlexaSkillsKit.Http\AlexaSkillsKit.Http.csproj", "{742331B9-5325-4F3F-B41F-EC41DFF9BD0C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AlexaSkillsKit", "AlexaSkillsKit.Lib\AlexaSkillsKit.csproj", "{01B7FA9F-5F29-46FA-8E27-B731A2A95876}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit", "AlexaSkillsKit.Lib\AlexaSkillsKit.csproj", "{01B7FA9F-5F29-46FA-8E27-B731A2A95876}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AlexaSkillsKit.Interfaces.AudioPlayer", "AlexaSkillsKit.Interfaces.AudioPlayer\AlexaSkillsKit.Interfaces.AudioPlayer.csproj", "{2D25EE87-D293-4A62-BCEC-AACB4099ED9E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Interfaces.AudioPlayer", "AlexaSkillsKit.Interfaces.AudioPlayer\AlexaSkillsKit.Interfaces.AudioPlayer.csproj", "{2D25EE87-D293-4A62-BCEC-AACB4099ED9E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AlexaSkillsKit.Interfaces.Display", "AlexaSkillsKit.Interfaces.Display\AlexaSkillsKit.Interfaces.Display.csproj", "{6224BE94-2A4B-4AB2-BA8A-4BD3DF47FC4F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Interfaces.Display", "AlexaSkillsKit.Interfaces.Display\AlexaSkillsKit.Interfaces.Display.csproj", "{6224BE94-2A4B-4AB2-BA8A-4BD3DF47FC4F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AlexaSkillsKit.Interfaces.Dialog", "AlexaSkillsKit.Interfaces.Dialog\AlexaSkillsKit.Interfaces.Dialog.csproj", "{39F798E3-70FE-419A-AF58-E0A181A42A40}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Interfaces.Dialog", "AlexaSkillsKit.Interfaces.Dialog\AlexaSkillsKit.Interfaces.Dialog.csproj", "{39F798E3-70FE-419A-AF58-E0A181A42A40}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlexaSkillsKit.Interfaces.VideoApp", "AlexaSkillsKit.Interfaces.VideoApp\AlexaSkillsKit.Interfaces.VideoApp.csproj", "{1517FE44-37AF-4585-AF54-0A20F222CFCB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AlexaSkillsKit.Interfaces.VideoApp", "AlexaSkillsKit.Interfaces.VideoApp\AlexaSkillsKit.Interfaces.VideoApp.csproj", "{1517FE44-37AF-4585-AF54-0A20F222CFCB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -33,10 +31,6 @@ Global {D0125A95-FD63-4A33-9220-206D4A1A14F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {D0125A95-FD63-4A33-9220-206D4A1A14F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {D0125A95-FD63-4A33-9220-206D4A1A14F0}.Release|Any CPU.Build.0 = Release|Any CPU - {742331B9-5325-4F3F-B41F-EC41DFF9BD0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {742331B9-5325-4F3F-B41F-EC41DFF9BD0C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {742331B9-5325-4F3F-B41F-EC41DFF9BD0C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {742331B9-5325-4F3F-B41F-EC41DFF9BD0C}.Release|Any CPU.Build.0 = Release|Any CPU {01B7FA9F-5F29-46FA-8E27-B731A2A95876}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {01B7FA9F-5F29-46FA-8E27-B731A2A95876}.Debug|Any CPU.Build.0 = Debug|Any CPU {01B7FA9F-5F29-46FA-8E27-B731A2A95876}.Release|Any CPU.ActiveCfg = Release|Any CPU From a591d496c87391d235f972585399cacd53069b4f Mon Sep 17 00:00:00 2001 From: Sergey Greenko Date: Sat, 23 Dec 2017 20:21:55 +0200 Subject: [PATCH 14/16] fix merge issues --- .../Speechlet/SpeechletService.cs | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs index e174dd2..1853cb4 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs @@ -37,18 +37,20 @@ public async Task GetRequestAsync(string content, stri validationResult |= SpeechletRequestValidationResult.NoSignatureHeader; } - var alexaBytes = Encoding.UTF8.GetBytes(content); - // attempt to verify signature only if we were able to locate certificate and signature headers if (validationResult == SpeechletRequestValidationResult.OK) { - if (!(await SpeechletRequestSignatureVerifier.VerifyRequestSignatureAsync(alexaBytes, signature, chainUrl))) { + var alexaBytes = Encoding.UTF8.GetBytes(content); + + if (!await SpeechletRequestSignatureVerifier.VerifyRequestSignatureAsync(alexaBytes, signature, chainUrl)) { validationResult |= SpeechletRequestValidationResult.InvalidSignature; } } - SpeechletRequestEnvelope alexaRequest = null; + SpeechletRequestEnvelope result = null; try { - alexaRequest = SpeechletRequestEnvelope.FromJson(RequestResolver, content); + + + result = SpeechletRequestEnvelope.FromJson(RequestResolver, content); } catch (SpeechletValidationException ex) { validationResult |= ex.ValidationResult; @@ -58,31 +60,34 @@ public async Task GetRequestAsync(string content, stri validationResult |= SpeechletRequestValidationResult.InvalidJson; } - DateTime now = DateTime.UtcNow; // reference time for this request + var success = false; // attempt to verify timestamp only if we were able to parse request body - if (alexaRequest != null) { - if (!SpeechletRequestTimestampVerifier.VerifyRequestTimestamp(alexaRequest, now)) { + if (result != null) { + var now = DateTime.UtcNow; // reference time for this request + + if (!SpeechletRequestTimestampVerifier.VerifyRequestTimestamp(result, now)) { validationResult |= SpeechletRequestValidationResult.InvalidTimestamp; } - } - var success = ValidationHandler?.Invoke(validationResult, now, alexaRequest) ?? (validationResult == SpeechletRequestValidationResult.OK); + success = ValidationHandler?.Invoke(validationResult, now, result) ?? (validationResult == SpeechletRequestValidationResult.OK); + } if (!success) { throw new SpeechletValidationException(validationResult); } - return alexaRequest; + return result; } + /// /// /// /// /// public async Task ProcessRequestAsync(SpeechletRequestEnvelope requestEnvelope) { - Session session = requestEnvelope.Session; + var session = requestEnvelope.Session; var context = requestEnvelope.Context; var request = requestEnvelope.Request; ISpeechletResponse response = null; @@ -113,9 +118,17 @@ public async Task ProcessRequestAsync(SpeechletReques return responseEnvelope; } + + /// + /// + /// private void DoSessionManagement(IntentRequest request, Session session) { if (request == null) return; + if (session.Attributes == null) { + session.Attributes = new Dictionary(); + } + if (session.IsNew) { session.Attributes[Session.INTENT_SEQUENCE] = request.Intent.Name; } From 19e987c0949f27eefbb6883d6e6fee5da5122a41 Mon Sep 17 00:00:00 2001 From: Sergey Greenko Date: Sat, 23 Dec 2017 20:24:52 +0200 Subject: [PATCH 15/16] fix one more merge issue --- AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs index 93086ac..bfa4f77 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs @@ -27,7 +27,7 @@ public string ProcessRequest(string requestContent) { } public async Task ProcessRequestAsync(string requestContent) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); + var requestEnvelope = SpeechletRequestEnvelope.FromJson(Service.RequestResolver, requestContent); return (await Service.ProcessRequestAsync(requestEnvelope))?.ToJson(); } @@ -42,7 +42,7 @@ public virtual string ProcessRequest(JObject requestJson) { } public async Task ProcessRequestAsync(JObject requestJson) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); + var requestEnvelope = SpeechletRequestEnvelope.FromJson(Service.RequestResolver, requestJson); return (await Service.ProcessRequestAsync(requestEnvelope))?.ToJson(); } From 404844c28c7db3be4a4094d14777ce379942d49f Mon Sep 17 00:00:00 2001 From: Sergey Greenko Date: Mon, 25 Dec 2017 14:25:14 +0200 Subject: [PATCH 16/16] Revert RequestParser usage --- .../AudioPlayerSpeechletServiceExtensions.cs | 4 ++-- .../DisplaySpeechletServiceExtensions.cs | 2 +- AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs | 10 ++++++---- .../Json/SpeechletRequestParserExtensions.cs | 4 ---- AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs | 4 ++-- AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs | 6 +----- .../Speechlet/SpeechletServiceExtensions.cs | 4 ++-- AlexaSkillsKit.Tests/Helpers/DateTimeHelpersTests.cs | 12 +++--------- 8 files changed, 17 insertions(+), 29 deletions(-) diff --git a/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerSpeechletServiceExtensions.cs b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerSpeechletServiceExtensions.cs index 0142733..d6501ab 100644 --- a/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerSpeechletServiceExtensions.cs +++ b/AlexaSkillsKit.Interfaces.AudioPlayer/AudioPlayerSpeechletServiceExtensions.cs @@ -12,7 +12,7 @@ public static void AddAudioPlayer(this SpeechletService service, IAudioPlayerSpe public static void AddAudioPlayer(this SpeechletService service, IAudioPlayerSpeechletAsync speechlet) { Deserializer.RegisterDeserializer("AudioPlayer", AudioPlayerInterface.FromJson); - service.RequestParser.AddInterface("AudioPlayer", (subtype, json) => { + SpeechletRequestEnvelope.RequestParser.AddInterface("AudioPlayer", (subtype, json) => { switch (subtype) { case "PlaybackFailed": return new AudioPlayerPlaybackFailedRequest(subtype, json); @@ -24,7 +24,7 @@ public static void AddAudioPlayer(this SpeechletService service, IAudioPlayerSpe service.AddHandler( async (request, context) => await speechlet.OnAudioPlayerAsync(request, context)); - service.RequestParser.AddInterface("PlaybackController", (subtype, json) => new PlaybackControllerRequest(subtype, json)); + SpeechletRequestEnvelope.RequestParser.AddInterface("PlaybackController", (subtype, json) => new PlaybackControllerRequest(subtype, json)); service.AddHandler( async (request, context) => await speechlet.OnPlaybackControllerAsync(request, context)); diff --git a/AlexaSkillsKit.Interfaces.Display/DisplaySpeechletServiceExtensions.cs b/AlexaSkillsKit.Interfaces.Display/DisplaySpeechletServiceExtensions.cs index fe966c5..b97a43d 100644 --- a/AlexaSkillsKit.Interfaces.Display/DisplaySpeechletServiceExtensions.cs +++ b/AlexaSkillsKit.Interfaces.Display/DisplaySpeechletServiceExtensions.cs @@ -12,7 +12,7 @@ public static void AddDisplay(this SpeechletService service, IDisplaySpeechlet s public static void AddDisplay(this SpeechletService service, IDisplaySpeechletAsync speechlet) { Deserializer.RegisterDeserializer("Display", DisplayInterface.FromJson); - service.RequestParser.AddInterface("Display", (subtype, json) => new DisplayRequest(subtype, json)); + SpeechletRequestEnvelope.RequestParser.AddInterface("Display", (subtype, json) => new DisplayRequest(subtype, json)); service.AddHandler(async (request, context) => await speechlet.OnDisplayAsync(request, context)); } diff --git a/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs b/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs index e911aff..72e2a1e 100644 --- a/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs +++ b/AlexaSkillsKit.Lib/Json/SpeechletRequestEnvelope.cs @@ -10,21 +10,23 @@ namespace AlexaSkillsKit.Json { public class SpeechletRequestEnvelope { + public static SpeechletRequestParser RequestParser { get; } = new SpeechletRequestParser(); + /// /// /// /// /// - public static SpeechletRequestEnvelope FromJson(SpeechletRequestParser parser, string content) { + public static SpeechletRequestEnvelope FromJson(string content) { if (String.IsNullOrEmpty(content)) { throw new SpeechletValidationException(SpeechletRequestValidationResult.NoContent, "Request content is empty"); } JObject json = JsonConvert.DeserializeObject(content, Sdk.DeserializationSettings); - return FromJson(parser, json); + return FromJson(json); } - public static SpeechletRequestEnvelope FromJson(SpeechletRequestParser parser, JObject json) { + public static SpeechletRequestEnvelope FromJson(JObject json) { var version = json.Value("version"); if (version != null && version != Sdk.VERSION) { throw new SpeechletValidationException(SpeechletRequestValidationResult.InvalidVersion, "Request must conform to 1.0 schema."); @@ -32,7 +34,7 @@ public static SpeechletRequestEnvelope FromJson(SpeechletRequestParser parser, J return new SpeechletRequestEnvelope { Version = version, - Request = parser.Parse(json.Value("request")), + Request = RequestParser.Parse(json.Value("request")), Session = Session.FromJson(json.Value("session")), Context = Context.FromJson(json.Value("context")) }; diff --git a/AlexaSkillsKit.Lib/Json/SpeechletRequestParserExtensions.cs b/AlexaSkillsKit.Lib/Json/SpeechletRequestParserExtensions.cs index 1f5b4e9..0489924 100644 --- a/AlexaSkillsKit.Lib/Json/SpeechletRequestParserExtensions.cs +++ b/AlexaSkillsKit.Lib/Json/SpeechletRequestParserExtensions.cs @@ -27,9 +27,5 @@ public static void AddSystem(this SpeechletRequestParser parser) { return null; }); } - - public static void AddDisplay(this SpeechletRequestParser parser) { - - } } } \ No newline at end of file diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs index ca39fe2..93086ac 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletBase.cs @@ -27,7 +27,7 @@ public string ProcessRequest(string requestContent) { } public async Task ProcessRequestAsync(string requestContent) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(Service.RequestParser, requestContent); + var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestContent); return (await Service.ProcessRequestAsync(requestEnvelope))?.ToJson(); } @@ -42,7 +42,7 @@ public virtual string ProcessRequest(JObject requestJson) { } public async Task ProcessRequestAsync(JObject requestJson) { - var requestEnvelope = SpeechletRequestEnvelope.FromJson(Service.RequestParser, requestJson); + var requestEnvelope = SpeechletRequestEnvelope.FromJson(requestJson); return (await Service.ProcessRequestAsync(requestEnvelope))?.ToJson(); } diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs index 272efec..1d5ae48 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletService.cs @@ -18,8 +18,6 @@ private IDictionary ValidationHandler { get; set; } - public SpeechletRequestParser RequestParser { get; } = new SpeechletRequestParser(); - public void AddHandler(Func> handler) where T : SpeechletRequest { handlers[typeof(T)] = async (request, session, context) => await handler(request as T, session, context); } @@ -50,9 +48,7 @@ public async Task GetRequestAsync(string content, stri SpeechletRequestEnvelope result = null; try { - - - result = SpeechletRequestEnvelope.FromJson(RequestParser, content); + result = SpeechletRequestEnvelope.FromJson(content); } catch (SpeechletValidationException ex) { validationResult |= ex.ValidationResult; diff --git a/AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs b/AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs index 14ce213..a088188 100644 --- a/AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs +++ b/AlexaSkillsKit.Lib/Speechlet/SpeechletServiceExtensions.cs @@ -16,7 +16,7 @@ public static void AddStandard(this SpeechletService service, ISpeechletAsync sp return null; }); - service.RequestParser.AddStandard(); + SpeechletRequestEnvelope.RequestParser.AddStandard(); } public static void AddStandard(this SpeechletService service, ISpeechlet speechlet) { @@ -29,7 +29,7 @@ public static void AddSystem(this SpeechletService service, ISystemSpeechletAsyn return null; }); - service.RequestParser.AddSystem(); + SpeechletRequestEnvelope.RequestParser.AddSystem(); } public static void AddSystem(this SpeechletService service, ISystemSpeechlet speechlet) { diff --git a/AlexaSkillsKit.Tests/Helpers/DateTimeHelpersTests.cs b/AlexaSkillsKit.Tests/Helpers/DateTimeHelpersTests.cs index 180622a..7ba5ffe 100644 --- a/AlexaSkillsKit.Tests/Helpers/DateTimeHelpersTests.cs +++ b/AlexaSkillsKit.Tests/Helpers/DateTimeHelpersTests.cs @@ -14,12 +14,10 @@ public void RequestWithIso8601TimestampTest() { SpeechletRequestValidationResult validationResult = SpeechletRequestValidationResult.OK; SpeechletRequestEnvelope alexaRequest = null; - var parser = new SpeechletRequestParser(); - parser.AddStandard(); var alexaContent = File.ReadAllText(TestDataFile); try { - alexaRequest = SpeechletRequestEnvelope.FromJson(parser, alexaContent); + alexaRequest = SpeechletRequestEnvelope.FromJson(alexaContent); } catch (Exception ex) when (ex is Newtonsoft.Json.JsonReaderException || ex is InvalidCastException || ex is FormatException) { @@ -36,12 +34,10 @@ public void RequestWithUnixTimeTimestampTest() { SpeechletRequestValidationResult validationResult = SpeechletRequestValidationResult.OK; SpeechletRequestEnvelope alexaRequest = null; - var parser = new SpeechletRequestParser(); - parser.AddStandard(); var alexaContent = File.ReadAllText(TestDataFile); try { - alexaRequest = SpeechletRequestEnvelope.FromJson(parser, alexaContent); + alexaRequest = SpeechletRequestEnvelope.FromJson(alexaContent); } catch (Exception ex) when (ex is Newtonsoft.Json.JsonReaderException || ex is InvalidCastException || ex is FormatException) { @@ -58,12 +54,10 @@ public void RequestWithInvalidTimestampTest(){ SpeechletRequestValidationResult validationResult = SpeechletRequestValidationResult.OK; SpeechletRequestEnvelope alexaRequest = null; - var parser = new SpeechletRequestParser(); - parser.AddStandard(); var alexaContent = File.ReadAllText(TestDataFile); try { - alexaRequest = SpeechletRequestEnvelope.FromJson(parser, alexaContent); + alexaRequest = SpeechletRequestEnvelope.FromJson(alexaContent); } catch (Exception ex) when (ex is Newtonsoft.Json.JsonReaderException || ex is InvalidCastException || ex is FormatException) {