diff --git a/Alexa.NET.Tests/Alexa.NET.Tests.csproj b/Alexa.NET.Tests/Alexa.NET.Tests.csproj index ae2964d..53fe502 100644 --- a/Alexa.NET.Tests/Alexa.NET.Tests.csproj +++ b/Alexa.NET.Tests/Alexa.NET.Tests.csproj @@ -20,6 +20,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -50,6 +53,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -62,15 +68,33 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/Alexa.NET.Tests/Examples/CompleteTaskDirective.json b/Alexa.NET.Tests/Examples/CompleteTaskDirective.json new file mode 100644 index 0000000..d0dc9c6 --- /dev/null +++ b/Alexa.NET.Tests/Examples/CompleteTaskDirective.json @@ -0,0 +1,7 @@ +{ + "type": "Tasks.CompleteTask", + "status": { + "code": 200, + "message": "return as desired" + } +} \ No newline at end of file diff --git a/Alexa.NET.Tests/Examples/LaunchRequestWithTask.json b/Alexa.NET.Tests/Examples/LaunchRequestWithTask.json new file mode 100644 index 0000000..56e0908 --- /dev/null +++ b/Alexa.NET.Tests/Examples/LaunchRequestWithTask.json @@ -0,0 +1,18 @@ +{ + "type": "LaunchRequest", + "requestId": "string", + "timestamp": "2019-07-03T00:00:00", + "locale": "string", + "originIpAddress": "string", + "task": { + "name": "AMAZON.PrintPDF", + "version": "1", + "input": { + "@type": "PrintPDFRequest", + "@version": "1", + "title": "Flywheel", + "description": "Flywheel", + "url": "http://www.example.com/flywheel.pdf" + } + } +} \ No newline at end of file diff --git a/Alexa.NET.Tests/Examples/PrintImageConnection.json b/Alexa.NET.Tests/Examples/PrintImageConnection.json new file mode 100644 index 0000000..f73bffc --- /dev/null +++ b/Alexa.NET.Tests/Examples/PrintImageConnection.json @@ -0,0 +1,12 @@ +{ + "type": "Connections.StartConnection", + "uri": "connection://AMAZON.PrintImage/1", + "input": { + "@type": "PrintImageRequest", + "@version": "1", + "title": "Flywheel Document", + "description": "Flywheel", + "imageType": "JPEG", + "url": "http://www.example.com/flywheel.jpeg" + } +} \ No newline at end of file diff --git a/Alexa.NET.Tests/Examples/PrintPDFConnection.json b/Alexa.NET.Tests/Examples/PrintPDFConnection.json new file mode 100644 index 0000000..1a406a3 --- /dev/null +++ b/Alexa.NET.Tests/Examples/PrintPDFConnection.json @@ -0,0 +1,15 @@ +{ + "type": "Connections.StartConnection", + "uri": "connection://AMAZON.PrintPDF/1", + "input": { + "@type": "PrintPDFRequest", + "@version": "1", + "title": "title", + "description": "description", + "context": { + "providerId": "your-provider-skill-id" + }, + "url": "http://www.example.com/flywheel.pdf" + }, + "token": "none" +} \ No newline at end of file diff --git a/Alexa.NET.Tests/Examples/PrintWebPageConnection.json b/Alexa.NET.Tests/Examples/PrintWebPageConnection.json new file mode 100644 index 0000000..c354542 --- /dev/null +++ b/Alexa.NET.Tests/Examples/PrintWebPageConnection.json @@ -0,0 +1,11 @@ +{ + "type": "Connections.StartConnection", + "uri": "connection://AMAZON.PrintWebPage/1", + "input": { + "@type": "PrintWebPageRequest", + "@version": "1", + "title": "title", + "description": "description", + "url": "http://www.example.com/flywheel.html" + } +} \ No newline at end of file diff --git a/Alexa.NET.Tests/Examples/ScheduleFoodEstablishmentReservation.json b/Alexa.NET.Tests/Examples/ScheduleFoodEstablishmentReservation.json new file mode 100644 index 0000000..369fdec --- /dev/null +++ b/Alexa.NET.Tests/Examples/ScheduleFoodEstablishmentReservation.json @@ -0,0 +1,24 @@ +{ + "type": "Connections.StartConnection", + "uri": "connection://AMAZON.ScheduleFoodEstablishmentReservation/1", + "input": { + "@type": "ScheduleFoodEstablishmentReservationRequest", + "@version": "1", + "startTime": "2018-04-08T01:15:46Z", + "partySize": 2, + "restaurant": { + "@type": "Restaurant", + "@version": "1", + "name": "Amazon Day 1 Restaurant", + "location": { + "@type": "PostalAddress", + "@version": "1", + "streetAddress": "2121 7th Avenue", + "locality": "Seattle", + "region": "WA", + "postalCode": "98121", + "country": "US" + } + } + } +} \ No newline at end of file diff --git a/Alexa.NET.Tests/Examples/ScheduleTaxiReservation.json b/Alexa.NET.Tests/Examples/ScheduleTaxiReservation.json new file mode 100644 index 0000000..5594bc0 --- /dev/null +++ b/Alexa.NET.Tests/Examples/ScheduleTaxiReservation.json @@ -0,0 +1,27 @@ +{ + "type": "Connections.StartConnection", + "uri": "connection://AMAZON.ScheduleTaxiReservation/1", + "input": { + "@type": "ScheduleTaxiReservationRequest", + "@version": "1", + "partySize": 4, + "pickupLocation": { + "@type": "PostalAddress", + "@version": "1", + "streetAddress": "415 106th Ave NE", + "locality": "Bellevue", + "region": "WA", + "postalCode": "98004", + "country": "US" + }, + "dropoffLocation": { + "@type": "PostalAddress", + "@version": "1", + "streetAddress": "2031 6th Ave.", + "locality": "Seattle", + "region": "WA", + "postalCode": "98121", + "country": "US" + } + } +} \ No newline at end of file diff --git a/Alexa.NET.Tests/Examples/SessionResumedRequest.json b/Alexa.NET.Tests/Examples/SessionResumedRequest.json new file mode 100644 index 0000000..ee08f10 --- /dev/null +++ b/Alexa.NET.Tests/Examples/SessionResumedRequest.json @@ -0,0 +1,16 @@ +{ + "type": "SessionResumedRequest", + "requestId": "string", + "timestamp": "2019-07-03T00:00:00", + "locale": "en-GB", + "originIpAddress": "string", + "cause": { + "type": "ConnectionCompleted", + "token": "1234", + "status": { + "code": 200, + "message": "OK" + }, + "result": null + } +} \ No newline at end of file diff --git a/Alexa.NET.Tests/SkillConnectionTests.cs b/Alexa.NET.Tests/SkillConnectionTests.cs new file mode 100644 index 0000000..08b72c1 --- /dev/null +++ b/Alexa.NET.Tests/SkillConnectionTests.cs @@ -0,0 +1,184 @@ +using System; +using Alexa.NET.ConnectionTasks; +using Alexa.NET.ConnectionTasks.Inputs; +using Alexa.NET.Request.Type; +using Alexa.NET.Response; +using Alexa.NET.Response.Directive; +using Xunit; + +namespace Alexa.NET.Tests +{ + public class SkillConnectionTests + { + [Fact] + public void StartConnectionDirectiveSerializesCorrectly() + { + var task = new PrintPdfV1 + { + Title = "title", + Description = "description", + Url = "http://www.example.com/flywheel.pdf", + Context = new ConnectionTaskContext { ProviderId = "your-provider-skill-id" } + }; + Utility.CompareJson(task.ToConnectionDirective("none"), "PrintPDFConnection.json"); + } + + [Fact] + public void StartConnectionDirectiveDeserializesCorrectly() + { + var raw = Utility.ExampleFileContent("PrintPDFConnection.json"); + Assert.NotNull(raw); + + var directive = Assert.IsType(raw); + Assert.Equal("none", directive.Token); + Assert.Equal(PrintPdfV1.AssociatedUri, directive.Uri); + + Assert.IsType(directive.Input); + } + + [Fact] + public void SessionResumedSerializesProperly() + { + var task = new SessionResumedRequest + { + RequestId = "string", + Timestamp = new DateTime(2019, 07, 03), + Locale = "en-GB", + OriginIpAddress = "string", + Cause = new SessionResumedRequestCause + { + Type = "ConnectionCompleted", + Token = "1234", + Status = new TaskStatus(200, "OK") + } + }; + Utility.CompareJson(task, "SessionResumedRequest.json"); + } + + [Fact] + public void SessionResumedDeserializesProperly() + { + var result = Utility.ExampleFileContent("SessionResumedRequest.json"); + var request = Assert.IsType(result); + + Assert.Equal("1234", request.Cause.Token); + Assert.Equal(200, request.Cause.Status.Code); + Assert.Equal("OK", request.Cause.Status.Message); + } + + [Fact] + public void LaunchRequestWithTaskDeserializesCorrectly() + { + var result = Utility.ExampleFileContent("LaunchRequestWithTask.json"); + Assert.NotNull(result.Task); + Assert.Equal("AMAZON.PrintPDF", result.Task.Name); + Assert.Equal("1", result.Task.Version); + Assert.IsType(result.Task.Input); + } + + [Fact] + public void TestCompleteTaskDirective() + { + var directive = new CompleteTaskDirective(200, "return as desired"); + Assert.True(Utility.CompareJson(directive, "CompleteTaskDirective.json")); + } + + [Fact] + public void PrintImageConnectionComparison() + { + var directive = new PrintImageV1 + { + Title = "Flywheel Document", + Description = "Flywheel", + ImageV1Type = PrintImageV1Type.JPEG, + Url = "http://www.example.com/flywheel.jpeg" + }.ToConnectionDirective(); + Assert.Equal(PrintImageV1.AssociatedUri, directive.Uri); + Assert.True(Utility.CompareJson(directive, "PrintImageConnection.json")); + Assert.IsType(Utility.ExampleFileContent("PrintImageConnection.json").Input); + } + + [Fact] + public void PrintPDFConnectionComparison() + { + var directive = new PrintPdfV1 + { + Title = "title", + Description = "description", + Url = "http://www.example.com/flywheel.pdf", + Context = new ConnectionTaskContext { ProviderId = "your-provider-skill-id" } + }.ToConnectionDirective("none"); + Assert.Equal(PrintPdfV1.AssociatedUri, directive.Uri); + Assert.True(Utility.CompareJson(directive, "PrintPDFConnection.json")); + Assert.IsType(Utility.ExampleFileContent("PrintPDFConnection.json").Input); + } + + [Fact] + public void PrintWebPageConnectionComparison() + { + var directive = new PrintWebPageV1 + { + Title = "title", + Description = "description", + Url = "http://www.example.com/flywheel.html" + }.ToConnectionDirective(); + Assert.Equal(PrintWebPageV1.AssociatedUri, directive.Uri); + Assert.True(Utility.CompareJson(directive, "PrintWebPageConnection.json")); + Assert.IsType(Utility.ExampleFileContent("PrintPDFConnection.json").Input); + } + + [Fact] + public void ScheduleTaxiReservationConnectionComparison() + { + var directive = new ScheduleTaxiReservation + { + PartySize = 4, + PickupLocation = new PostalAddress + { + StreetAddress = "415 106th Ave NE", + Locality = "Bellevue", + Region = "WA", + PostalCode = "98004", + Country = "US" + }, + DropoffLocation = new PostalAddress + { + StreetAddress = "2031 6th Ave.", + Locality = "Seattle", + Region = "WA", + PostalCode = "98121", + Country = "US" + } + }.ToConnectionDirective(); + Assert.Equal(ScheduleTaxiReservation.AssociatedUri, directive.Uri); + Assert.True(Utility.CompareJson(directive, "ScheduleTaxiReservation.json")); + Assert.IsType(Utility.ExampleFileContent("ScheduleTaxiReservation.json").Input); + } + + [Fact] + public void ScheduleFoodReservationConnectionComparison() + { + var directive = new ScheduleFoodEstablishmentReservation + { + PartySize = 2, + StartTime = new DateTime(2018,04,08,01,15,46), + Restaurant = new Restaurant + { + Name = "Amazon Day 1 Restaurant", + Location = new PostalAddress + { + StreetAddress = "2121 7th Avenue", + Locality = "Seattle", + Region = "WA", + PostalCode = "98121", + Country = "US" + } + } + }.ToConnectionDirective(); + Assert.Equal(ScheduleFoodEstablishmentReservation.AssociatedUri, directive.Uri); + Assert.True(Utility.CompareJson(directive, "ScheduleFoodEstablishmentReservation.json")); + Assert.IsType(Utility.ExampleFileContent("ScheduleFoodEstablishmentReservation.json").Input); + } + + } +} diff --git a/Alexa.NET/Alexa.NET.csproj b/Alexa.NET/Alexa.NET.csproj index 22081ec..2c7941a 100644 --- a/Alexa.NET/Alexa.NET.csproj +++ b/Alexa.NET/Alexa.NET.csproj @@ -3,13 +3,13 @@ A simple .NET Core library for handling Alexa Skill request/responses. Alexa.NET - 1.6.3 + 1.7.0 Tim Heuer, Steven Pears netstandard1.6 Alexa.NET Alexa.NET amazon;alexa;echo;dot;echo dot;skills - Added SessionEnd error object; Fix deserialization error; Added permission scopes (by @VinceGusmini); Added response type converters (by @rdlaitila); Added Dynamic Entity support (by @stoiveyp) + Added core support for Skill connections (by @stoiveyp) and in association with Alexa.NET.Management enables the end-to-end. https://github.com/timheuer/alexa-skills-dotnet MIT https://github.com/timheuer/alexa-skills-dotnet diff --git a/Alexa.NET/ConnectionTasks/ConnectionTaskContext.cs b/Alexa.NET/ConnectionTasks/ConnectionTaskContext.cs new file mode 100644 index 0000000..6728312 --- /dev/null +++ b/Alexa.NET/ConnectionTasks/ConnectionTaskContext.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Alexa.NET.ConnectionTasks +{ + public class ConnectionTaskContext + { + [JsonProperty("providerId")] + public string ProviderId { get; set; } + } +} \ No newline at end of file diff --git a/Alexa.NET/ConnectionTasks/IConnectionTask.cs b/Alexa.NET/ConnectionTasks/IConnectionTask.cs new file mode 100644 index 0000000..e1a79aa --- /dev/null +++ b/Alexa.NET/ConnectionTasks/IConnectionTask.cs @@ -0,0 +1,22 @@ +using System.Text; +using Alexa.NET.Response.Converters; +using Newtonsoft.Json; + +namespace Alexa.NET.ConnectionTasks +{ + [JsonConverter(typeof(ConnectionTaskConverter))] + public interface IConnectionTask + { + [JsonIgnore] + string ConnectionUri { get; } + + [JsonProperty("@type")] + string Type { get; } + + [JsonProperty("@version")] + string Version { get; } + + [JsonProperty("context",NullValueHandling = NullValueHandling.Ignore)] + ConnectionTaskContext Context { get; set; } + } +} diff --git a/Alexa.NET/ConnectionTasks/IConnectionTaskExtensions.cs b/Alexa.NET/ConnectionTasks/IConnectionTaskExtensions.cs new file mode 100644 index 0000000..1cdac28 --- /dev/null +++ b/Alexa.NET/ConnectionTasks/IConnectionTaskExtensions.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Alexa.NET.Response.Directive; + +namespace Alexa.NET.ConnectionTasks +{ + public static class IConnectionTaskExtensions + { + + public static StartConnectionDirective ToConnectionDirective(this IConnectionTask task, string token = null) + { + return new StartConnectionDirective(task, token); + } + } +} diff --git a/Alexa.NET/ConnectionTasks/Inputs/PostalAddress.cs b/Alexa.NET/ConnectionTasks/Inputs/PostalAddress.cs new file mode 100644 index 0000000..aaf8f86 --- /dev/null +++ b/Alexa.NET/ConnectionTasks/Inputs/PostalAddress.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json; + +namespace Alexa.NET.ConnectionTasks.Inputs +{ + public class PostalAddress + { + [JsonProperty("@type")] public string Type => "PostalAddress"; + [JsonProperty("@version")] public string Version = 1.ToString(); + [JsonProperty("streetAddress")] public string StreetAddress { get; set; } + [JsonProperty("locality")] public string Locality { get; set; } + [JsonProperty("region")] public string Region { get; set; } + [JsonProperty("postalCode")] public string PostalCode { get; set; } + [JsonProperty("country", NullValueHandling = NullValueHandling.Ignore)] public string Country { get; set; } + } +} diff --git a/Alexa.NET/ConnectionTasks/Inputs/PrintImageV1.cs b/Alexa.NET/ConnectionTasks/Inputs/PrintImageV1.cs new file mode 100644 index 0000000..c7a0658 --- /dev/null +++ b/Alexa.NET/ConnectionTasks/Inputs/PrintImageV1.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Alexa.NET.ConnectionTasks.Inputs +{ + public class PrintImageV1 : IConnectionTask + { + public const string AssociatedUri = "connection://AMAZON.PrintImage/1"; + [JsonIgnore] + public string ConnectionUri => AssociatedUri; + + [JsonProperty("@type")] + public string Type => "PrintImageRequest"; + + [JsonProperty("@version")] + public string Version => 1.ToString(); + + [JsonProperty("context", NullValueHandling = NullValueHandling.Ignore)] + public ConnectionTaskContext Context { get; set; } + + [JsonProperty("title")] + public string Title { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("imageType"),JsonConverter(typeof(StringEnumConverter))] + public PrintImageV1Type ImageV1Type { get; set; } + + [JsonProperty("url")] + public string Url { get; set; } + } + + public enum PrintImageV1Type + { + JPG, + JPEG + } +} diff --git a/Alexa.NET/ConnectionTasks/Inputs/PrintPdfV1.cs b/Alexa.NET/ConnectionTasks/Inputs/PrintPdfV1.cs new file mode 100644 index 0000000..453f508 --- /dev/null +++ b/Alexa.NET/ConnectionTasks/Inputs/PrintPdfV1.cs @@ -0,0 +1,32 @@ +using System; +using Newtonsoft.Json; + +namespace Alexa.NET.ConnectionTasks.Inputs +{ + public class PrintPdfV1:IConnectionTask + { + public const string AssociatedUri = "connection://AMAZON.PrintPDF/1"; + + [JsonIgnore] + public string ConnectionUri => AssociatedUri; + + [JsonProperty("@type")] + public string Type => "PrintPDFRequest"; + + [JsonProperty("context", NullValueHandling = NullValueHandling.Ignore)] + public ConnectionTaskContext Context { get; set; } + + [JsonProperty("@version")] + public string Version => 1.ToString(); + + [JsonProperty("title")] + public string Title { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("url")] + public string Url { get; set; } + + } +} diff --git a/Alexa.NET/ConnectionTasks/Inputs/PrintWebPageV1.cs b/Alexa.NET/ConnectionTasks/Inputs/PrintWebPageV1.cs new file mode 100644 index 0000000..113ac30 --- /dev/null +++ b/Alexa.NET/ConnectionTasks/Inputs/PrintWebPageV1.cs @@ -0,0 +1,32 @@ +using System; +using Newtonsoft.Json; + +namespace Alexa.NET.ConnectionTasks.Inputs +{ + public class PrintWebPageV1:IConnectionTask + { + public const string AssociatedUri = "connection://AMAZON.PrintWebPage/1"; + + [JsonIgnore] + public string ConnectionUri => AssociatedUri; + + [JsonProperty("@type")] + public string Type => "PrintWebPageRequest"; + + [JsonProperty("context", NullValueHandling = NullValueHandling.Ignore)] + public ConnectionTaskContext Context { get; set; } + + [JsonProperty("@version")] + public string Version => 1.ToString(); + + [JsonProperty("title")] + public string Title { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("url")] + public string Url { get; set; } + + } +} diff --git a/Alexa.NET/ConnectionTasks/Inputs/Restaurant.cs b/Alexa.NET/ConnectionTasks/Inputs/Restaurant.cs new file mode 100644 index 0000000..c3786d8 --- /dev/null +++ b/Alexa.NET/ConnectionTasks/Inputs/Restaurant.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace Alexa.NET.ConnectionTasks.Inputs +{ + public class Restaurant + { + [JsonProperty("@type")] + public string Type => "Restaurant"; + + [JsonProperty("@version")] + public string Version => 1.ToString(); + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("location")] + public PostalAddress Location { get; set; } + } +} \ No newline at end of file diff --git a/Alexa.NET/ConnectionTasks/Inputs/ScheduleFoodEstablishmentReservation.cs b/Alexa.NET/ConnectionTasks/Inputs/ScheduleFoodEstablishmentReservation.cs new file mode 100644 index 0000000..4bd46dc --- /dev/null +++ b/Alexa.NET/ConnectionTasks/Inputs/ScheduleFoodEstablishmentReservation.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Alexa.NET.Helpers; +using Newtonsoft.Json; + +namespace Alexa.NET.ConnectionTasks.Inputs +{ + public class ScheduleFoodEstablishmentReservation:IConnectionTask + { + public const string AssociatedUri = "connection://AMAZON.ScheduleFoodEstablishmentReservation/1"; + [JsonIgnore] + public string ConnectionUri => AssociatedUri; + + [JsonProperty("@type")] + public string Type => "ScheduleFoodEstablishmentReservationRequest"; + + [JsonProperty("@version")] + public string Version => 1.ToString(); + + [JsonProperty("context", NullValueHandling = NullValueHandling.Ignore)] + public ConnectionTaskContext Context { get; set; } + + [JsonProperty("partySize",NullValueHandling = NullValueHandling.Ignore)] + public int PartySize { get; set; } + + [JsonProperty("startTime",NullValueHandling = NullValueHandling.Ignore),JsonConverter(typeof(MixedDateTimeConverter))] + public DateTime? StartTime { get; set; } + + [JsonProperty("restaurant")] + public Restaurant Restaurant { get; set; } + } +} diff --git a/Alexa.NET/ConnectionTasks/Inputs/ScheduleTaxiReservation.cs b/Alexa.NET/ConnectionTasks/Inputs/ScheduleTaxiReservation.cs new file mode 100644 index 0000000..2a16793 --- /dev/null +++ b/Alexa.NET/ConnectionTasks/Inputs/ScheduleTaxiReservation.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Alexa.NET.Helpers; +using Newtonsoft.Json; + +namespace Alexa.NET.ConnectionTasks.Inputs +{ + public class ScheduleTaxiReservation:IConnectionTask + { + public const string AssociatedUri = "connection://AMAZON.ScheduleTaxiReservation/1"; + [JsonIgnore] + public string ConnectionUri => AssociatedUri; + + [JsonProperty("@type")] + public string Type => "ScheduleTaxiReservationRequest"; + + [JsonProperty("@version")] + public string Version => 1.ToString(); + + [JsonProperty("context", NullValueHandling = NullValueHandling.Ignore)] + public ConnectionTaskContext Context { get; set; } + + [JsonProperty("partySize")] + public int PartySize { get; set; } + + [JsonProperty("pickupLocation",NullValueHandling = NullValueHandling.Ignore)] + public PostalAddress PickupLocation { get; set; } + + [JsonProperty("dropoffLocation",NullValueHandling = NullValueHandling.Ignore)] + public PostalAddress DropoffLocation { get; set; } + + [JsonProperty("pickupTime",NullValueHandling = NullValueHandling.Ignore),JsonConverter(typeof(MixedDateTimeConverter))] + public DateTime? PickupTime { get; set; } + } +} diff --git a/Alexa.NET/ConnectionTasks/TaskStatus.cs b/Alexa.NET/ConnectionTasks/TaskStatus.cs new file mode 100644 index 0000000..7b86f44 --- /dev/null +++ b/Alexa.NET/ConnectionTasks/TaskStatus.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; + +namespace Alexa.NET.ConnectionTasks +{ + public class TaskStatus + { + public TaskStatus() { } + + public TaskStatus(int code, string message) + { + Code = code; + Message = message; + } + + [JsonProperty("code")] + public int Code { get; set; } + + [JsonProperty("message")] + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/Alexa.NET/Request/SkillRequest.cs b/Alexa.NET/Request/SkillRequest.cs index bed96f7..07f0eea 100644 --- a/Alexa.NET/Request/SkillRequest.cs +++ b/Alexa.NET/Request/SkillRequest.cs @@ -15,7 +15,6 @@ public class SkillRequest public Context Context { get; set; } [JsonProperty("request")] - [JsonConverter(typeof(RequestConverter))] public Type.Request Request { get; set; } public System.Type GetRequestType() diff --git a/Alexa.NET/Request/Type/Converters/SkillConnectionRequestTypeConverter.cs b/Alexa.NET/Request/Type/Converters/SkillConnectionRequestTypeConverter.cs new file mode 100644 index 0000000..b7f1614 --- /dev/null +++ b/Alexa.NET/Request/Type/Converters/SkillConnectionRequestTypeConverter.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Alexa.NET.Request.Type +{ + public class SkillConnectionRequestTypeConverter:IRequestTypeConverter + { + public bool CanConvert(string requestType) + { + return requestType == "SessionResumedRequest"; + } + + public Request Convert(string requestType) + { + return new SessionResumedRequest(); + } + } +} diff --git a/Alexa.NET/Request/Type/LaunchRequest.cs b/Alexa.NET/Request/Type/LaunchRequest.cs index e31c999..795899e 100644 --- a/Alexa.NET/Request/Type/LaunchRequest.cs +++ b/Alexa.NET/Request/Type/LaunchRequest.cs @@ -1,6 +1,10 @@ -namespace Alexa.NET.Request.Type +using Newtonsoft.Json; + +namespace Alexa.NET.Request.Type { public class LaunchRequest : Request { + [JsonProperty("task",NullValueHandling = NullValueHandling.Ignore)] + public LaunchRequestTask Task { get; set; } } } \ No newline at end of file diff --git a/Alexa.NET/Request/Type/LaunchRequestTask.cs b/Alexa.NET/Request/Type/LaunchRequestTask.cs new file mode 100644 index 0000000..17bfa1a --- /dev/null +++ b/Alexa.NET/Request/Type/LaunchRequestTask.cs @@ -0,0 +1,17 @@ +using Alexa.NET.ConnectionTasks; +using Newtonsoft.Json; + +namespace Alexa.NET.Request.Type +{ + public class LaunchRequestTask + { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("version")] + public string Version { get; set; } + + [JsonProperty("input")] + public IConnectionTask Input { get; set; } + } +} \ No newline at end of file diff --git a/Alexa.NET/Request/Type/Request.cs b/Alexa.NET/Request/Type/Request.cs index 42bec23..69f460d 100644 --- a/Alexa.NET/Request/Type/Request.cs +++ b/Alexa.NET/Request/Type/Request.cs @@ -4,6 +4,7 @@ namespace Alexa.NET.Request.Type { + [JsonConverter(typeof(RequestConverter))] public abstract class Request { [JsonProperty("type")] diff --git a/Alexa.NET/Request/Type/RequestConverter.cs b/Alexa.NET/Request/Type/RequestConverter.cs index c3674b9..9dbcde3 100644 --- a/Alexa.NET/Request/Type/RequestConverter.cs +++ b/Alexa.NET/Request/Type/RequestConverter.cs @@ -14,7 +14,8 @@ public class RequestConverter : JsonConverter new AudioPlayerRequestTypeConverter(), new PlaybackRequestTypeConverter(), new TemplateEventRequestTypeConverter(), - new SkillEventRequestTypeConverter() + new SkillEventRequestTypeConverter(), + new SkillConnectionRequestTypeConverter() }); public override bool CanWrite => false; diff --git a/Alexa.NET/Request/Type/SessionResumedRequest.cs b/Alexa.NET/Request/Type/SessionResumedRequest.cs new file mode 100644 index 0000000..4e8f13e --- /dev/null +++ b/Alexa.NET/Request/Type/SessionResumedRequest.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Alexa.NET.Request.Type +{ + public class SessionResumedRequest:Request + { + [JsonProperty("originIpAddress")] + public string OriginIpAddress { get; set; } + + [JsonProperty("cause")] + public SessionResumedRequestCause Cause { get; set; } + } +} diff --git a/Alexa.NET/Request/Type/SessionResumedRequestCause.cs b/Alexa.NET/Request/Type/SessionResumedRequestCause.cs new file mode 100644 index 0000000..cd12579 --- /dev/null +++ b/Alexa.NET/Request/Type/SessionResumedRequestCause.cs @@ -0,0 +1,20 @@ +using Alexa.NET.ConnectionTasks; +using Newtonsoft.Json; + +namespace Alexa.NET.Request.Type +{ + public class SessionResumedRequestCause + { + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("token")] + public string Token { get; set; } + + [JsonProperty("status")] + public TaskStatus Status { get; set; } + + [JsonProperty("result")] + public object Result { get; set; } + } +} \ No newline at end of file diff --git a/Alexa.NET/Response/Converters/ConnectionTaskConverter.cs b/Alexa.NET/Response/Converters/ConnectionTaskConverter.cs new file mode 100644 index 0000000..b3b7d58 --- /dev/null +++ b/Alexa.NET/Response/Converters/ConnectionTaskConverter.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using Alexa.NET.ConnectionTasks; +using Alexa.NET.ConnectionTasks.Inputs; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Alexa.NET.Response.Converters +{ + public class ConnectionTaskConverter : JsonConverter + { + public static Dictionary> TaskFactoryFromUri = new Dictionary> + { + {"PrintPDFRequest/1",() => new PrintPdfV1() }, + {"PrintImageRequest/1", () => new PrintImageV1() }, + {"PrintWebPageRequest/1",() => new PrintWebPageV1()}, + {"ScheduleTaxiReservationRequest/1",() => new ScheduleTaxiReservation() }, + {"ScheduleFoodEstablishmentReservationRequest/1",() => new ScheduleFoodEstablishmentReservation()} + }; + + public override bool CanRead => true; + public override bool CanWrite => false; + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var jsonObject = JObject.Load(reader); + var typeKey = jsonObject.Value("@type"); + var versionKey = jsonObject.Value("@version"); + var factoryKey = $"{typeKey}/{versionKey}"; + var hasFactory = TaskFactoryFromUri.ContainsKey(factoryKey); + + if (!hasFactory) + throw new Exception( + $"unable to deserialize response. " + + $"unrecognized task type '{typeKey}' with version '{versionKey}'" + ); + + var directive = TaskFactoryFromUri[factoryKey](); + + serializer.Populate(jsonObject.CreateReader(), directive); + + return directive; + } + + public override bool CanConvert(Type objectType) + { + return objectType == typeof(IConnectionTask); + } + } +} \ No newline at end of file diff --git a/Alexa.NET/Response/Converters/DirectiveConverter.cs b/Alexa.NET/Response/Converters/DirectiveConverter.cs index d98428e..9887c5c 100644 --- a/Alexa.NET/Response/Converters/DirectiveConverter.cs +++ b/Alexa.NET/Response/Converters/DirectiveConverter.cs @@ -24,6 +24,8 @@ public class DirectiveConverter : JsonConverter { "Hint", () => new HintDirective() }, { "AudioPlayer.Stop", () => new StopDirective() }, { "VideoApp.Launch", () => new VideoAppDirective() }, + { "Connections.StartConnection", () => new StartConnectionDirective() }, + { "Tasks.CompleteTask",() => new CompleteTaskDirective()} }; public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) diff --git a/Alexa.NET/Response/Directive/CompleteTaskDirective.cs b/Alexa.NET/Response/Directive/CompleteTaskDirective.cs new file mode 100644 index 0000000..f924d85 --- /dev/null +++ b/Alexa.NET/Response/Directive/CompleteTaskDirective.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Alexa.NET.ConnectionTasks; +using Newtonsoft.Json; + +namespace Alexa.NET.Response.Directive +{ + public class CompleteTaskDirective:IDirective + { + public CompleteTaskDirective() { } + + public CompleteTaskDirective(int statusCode, string statusMessage) + { + Status = new TaskStatus(statusCode,statusMessage); + } + + [JsonProperty("type")] + public string Type => "Tasks.CompleteTask"; + + [JsonProperty("status")] + public TaskStatus Status { get; set; } + } +} diff --git a/Alexa.NET/Response/Directive/StartConnectionDirective.cs b/Alexa.NET/Response/Directive/StartConnectionDirective.cs new file mode 100644 index 0000000..de5706a --- /dev/null +++ b/Alexa.NET/Response/Directive/StartConnectionDirective.cs @@ -0,0 +1,30 @@ +using System; +using Alexa.NET.ConnectionTasks; +using Newtonsoft.Json; + +namespace Alexa.NET.Response.Directive +{ + public class StartConnectionDirective:IDirective + { + [JsonProperty("type")] + public string Type => "Connections.StartConnection"; + + [JsonProperty("uri")] + public string Uri { get; set; } + + [JsonProperty("input")] + public IConnectionTask Input { get; set; } + + [JsonProperty("token",NullValueHandling = NullValueHandling.Ignore)] + public string Token { get; set; } + + public StartConnectionDirective(){} + + public StartConnectionDirective(IConnectionTask input, string token) + { + this.Uri = input.ConnectionUri; + this.Input = input; + this.Token = token; + } + } +} diff --git a/Alexa.NET/Response/ProgressiveResponse.cs b/Alexa.NET/Response/ProgressiveResponse.cs index dc8c317..3655873 100644 --- a/Alexa.NET/Response/ProgressiveResponse.cs +++ b/Alexa.NET/Response/ProgressiveResponse.cs @@ -69,14 +69,14 @@ public ProgressiveResponse() } - public Task SendSpeech(Ssml.Speech speech) + public Task SendSpeech(Ssml.Speech ssml) { - return Send(new VoicePlayerSpeakDirective(speech)); + return Send(new VoicePlayerSpeakDirective(ssml)); } - public Task SendSpeech(string speech) + public Task SendSpeech(string ssml) { - return Send(new VoicePlayerSpeakDirective(speech)); + return Send(new VoicePlayerSpeakDirective(ssml)); } public bool CanSend()