diff --git a/Shared/Models/Requests/RequestTypes/RequestBundle.cs b/Shared/Models/Requests/RequestTypes/RequestBundle.cs index 4d25377..dce2119 100644 --- a/Shared/Models/Requests/RequestTypes/RequestBundle.cs +++ b/Shared/Models/Requests/RequestTypes/RequestBundle.cs @@ -25,7 +25,34 @@ public class RequestBundle : IIntentRequest, ILaunchRequest, ISessionEndedReques /// ISO 8601 formatted string (for example, 2015-05-13T12:34:56Z). /// [JsonProperty("timestamp")] - public DateTime Timestamp { get; set; } + private string TimeStampString { get; set; } + + [JsonIgnore] + public DateTime Timestamp { + get + { + DateTime output; + var dateString = TimeStampString; + long temp; + if (long.TryParse(dateString, out temp)) + { + output = new DateTime(1970, 1, 1).AddMilliseconds(temp); + } + else + { + try + { + output = (DateTime)Newtonsoft.Json.JsonConvert.DeserializeObject(dateString, typeof(DateTime)); + } + catch (Exception e) + { + output = DateTime.MinValue; + } + } + return output; + } + set { TimeStampString = JsonConvert.SerializeObject(value); } + } /// /// An object that represents what the user wants. @@ -59,5 +86,13 @@ public Type GetRequestType() throw new ArgumentOutOfRangeException(nameof(Type), $"Unknown request type: {Type}."); } } + + [Newtonsoft.Json.Serialization.OnError] + internal void OnError( + System.Runtime.Serialization.StreamingContext context, + Newtonsoft.Json.Serialization.ErrorContext errorContext) + { + errorContext.Handled = true; + } } } \ No newline at end of file diff --git a/Slight.Alexa.Framework.Core.Tests/Slight.Alexa.Framework.Core.Tests.csproj b/Slight.Alexa.Framework.Core.Tests/Slight.Alexa.Framework.Core.Tests.csproj new file mode 100644 index 0000000..9112e00 --- /dev/null +++ b/Slight.Alexa.Framework.Core.Tests/Slight.Alexa.Framework.Core.Tests.csproj @@ -0,0 +1,50 @@ + + + + netcoreapp1.1 + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + ModelTests\RequestTests.cs + + + ModelTests\ResponseTests.cs + + + diff --git a/Slight.Alexa.Framework.Core/Slight.Alexa.Framework.Core.csproj b/Slight.Alexa.Framework.Core/Slight.Alexa.Framework.Core.csproj new file mode 100755 index 0000000..5439aea --- /dev/null +++ b/Slight.Alexa.Framework.Core/Slight.Alexa.Framework.Core.csproj @@ -0,0 +1,30 @@ + + + + A set of managed C# models and helpers to integrate Amazon's Alexa skill services into your .NET Core stack. + Slight.Alexa.Core + 1.0.10-beta + Silvenga + netstandard1.6 + Slight.Alexa.Core + Slight.Alexa.Framework.Core + amazon;alexa;echo + https://github.com/Silvenga/Slight.Alexa + https://github.com/Silvenga/Slight.Alexa/blob/master/LICENSE + https://github.com/Silvenga/Slight.Alexa + 1.6.0 + $(PackageTargetFallback);dnxcore50 + false + false + false + + + + + + + + + + + diff --git a/Slight.Alexa.Framework.Tests/ModelTests/Examples/IntentRequestWithInvalidTimestamp.json b/Slight.Alexa.Framework.Tests/ModelTests/Examples/IntentRequestWithInvalidTimestamp.json new file mode 100644 index 0000000..9eecc87 --- /dev/null +++ b/Slight.Alexa.Framework.Tests/ModelTests/Examples/IntentRequestWithInvalidTimestamp.json @@ -0,0 +1,34 @@ +{ + "version": "1.0", + "session": { + "new": false, + "sessionId": "amzn1.echo-api.session.0000000-0000-0000-0000-00000000000", + "application": { + "applicationId": "amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00ebe" + }, + "attributes": { + "supportedHoroscopePeriods": { + "daily": true, + "weekly": false, + "monthly": false + } + }, + "user": { + "userId": "amzn1.account.AM3B00000000000000000000000" + } + }, + "request": { + "type": "IntentRequest", + "requestId": " amzn1.echo-api.request.0000000-0000-0000-0000-00000000000", + "timestamp": "1499894026307", + "intent": { + "name": "GetZodiacHoroscopeIntent", + "slots": { + "ZodiacSign": { + "name": "ZodiacSign", + "value": "virgo" + } + } + } + } +} \ No newline at end of file diff --git a/Slight.Alexa.Framework.Tests/ModelTests/RequestTests.cs b/Slight.Alexa.Framework.Tests/ModelTests/RequestTests.cs index 2d5296c..428d857 100644 --- a/Slight.Alexa.Framework.Tests/ModelTests/RequestTests.cs +++ b/Slight.Alexa.Framework.Tests/ModelTests/RequestTests.cs @@ -11,7 +11,23 @@ namespace Slight.Alexa.Framework.Tests.ModelTests { public class RequestTests { - private const string ExamplesPath = @"ModelTests\Examples\"; + private readonly string ExamplesPath = @"ModelTests" + System.IO.Path.DirectorySeparatorChar + "Examples"; + + // The Alexa test console (as of 2017-07-13) sends the request timestamp in an invalid format: + // instead of an ISO 8601 date, it sends the milliseconds since 1970-1-1 (such as what's used + // for the Date object's ctor. + [Fact] + public void Can_handle_Test_Console_Invalid_Date() + { + const string example = "IntentRequestWithInvalidTimestamp.json"; + var json = File.ReadAllText(Path.Combine(ExamplesPath, example)); + var convertedObj = JsonConvert.DeserializeObject(json); + + Assert.NotNull(convertedObj); + Assert.Equal(typeof(IIntentRequest), convertedObj.Request.GetRequestType()); + Assert.Equal(typeof(IIntentRequest), convertedObj.GetRequestType()); + Assert.NotEqual(System.DateTime.MinValue, convertedObj.Request.Timestamp); + } [Fact] public void Can_read_IntentRequest_example() diff --git a/Slight.Alexa.Framework.Tests/ModelTests/ResponseTests.cs b/Slight.Alexa.Framework.Tests/ModelTests/ResponseTests.cs index cc2c153..791f60c 100644 --- a/Slight.Alexa.Framework.Tests/ModelTests/ResponseTests.cs +++ b/Slight.Alexa.Framework.Tests/ModelTests/ResponseTests.cs @@ -13,7 +13,7 @@ namespace Slight.Alexa.Framework.Tests.ModelTests { public class ResponseTests { - private const string ExamplesPath = @"ModelTests\Examples\"; + private readonly string ExamplesPath = @"ModelTests" + System.IO.Path.DirectorySeparatorChar + "Examples"; [Fact] public void Should_create_same_json_response_as_example() @@ -55,6 +55,7 @@ public void Should_create_same_json_response_as_example() }); const string example = "Response.json"; + var workingJson = File.ReadAllText(Path.Combine(ExamplesPath, example)); workingJson = Regex.Replace(workingJson, @"\s", ""); diff --git a/Slight.Alexa.sln b/Slight.Alexa.sln index 6210888..051d76c 100644 --- a/Slight.Alexa.sln +++ b/Slight.Alexa.sln @@ -7,8 +7,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slight.Alexa.Framework", "S EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slight.Alexa.Framework.Tests", "Slight.Alexa.Framework.Tests\Slight.Alexa.Framework.Tests.csproj", "{77836C05-6D66-4D93-BE54-55867F60CBC9}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Slight.Alexa.Framework.Core", "Slight.Alexa.Framework.Core\Slight.Alexa.Framework.Core.xproj", "{833E0D67-4325-4901-91CC-8588C2D28B26}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4BC0E6D4-3CA0-4CEB-9E3B-5B665177F6BE}" ProjectSection(SolutionItems) = preProject global.json = global.json @@ -16,6 +14,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Shared", "Shared\Shared.shproj", "{BEDCF7DE-8146-4187-AFEC-8B93EF15B6D6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slight.Alexa.Framework.Core", "Slight.Alexa.Framework.Core\Slight.Alexa.Framework.Core.csproj", "{BDB7F2ED-E783-4F84-A975-1A2AA4202348}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slight.Alexa.Framework.Core.Tests", "Slight.Alexa.Framework.Core.Tests\Slight.Alexa.Framework.Core.Tests.csproj", "{5E08693F-5986-4FB5-81FC-45DD17F0FF1C}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution Shared\Shared.projitems*{bedcf7de-8146-4187-afec-8b93ef15b6d6}*SharedItemsImports = 13 @@ -34,10 +36,14 @@ Global {77836C05-6D66-4D93-BE54-55867F60CBC9}.Debug|Any CPU.Build.0 = Debug|Any CPU {77836C05-6D66-4D93-BE54-55867F60CBC9}.Release|Any CPU.ActiveCfg = Release|Any CPU {77836C05-6D66-4D93-BE54-55867F60CBC9}.Release|Any CPU.Build.0 = Release|Any CPU - {833E0D67-4325-4901-91CC-8588C2D28B26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {833E0D67-4325-4901-91CC-8588C2D28B26}.Debug|Any CPU.Build.0 = Debug|Any CPU - {833E0D67-4325-4901-91CC-8588C2D28B26}.Release|Any CPU.ActiveCfg = Release|Any CPU - {833E0D67-4325-4901-91CC-8588C2D28B26}.Release|Any CPU.Build.0 = Release|Any CPU + {BDB7F2ED-E783-4F84-A975-1A2AA4202348}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BDB7F2ED-E783-4F84-A975-1A2AA4202348}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BDB7F2ED-E783-4F84-A975-1A2AA4202348}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BDB7F2ED-E783-4F84-A975-1A2AA4202348}.Release|Any CPU.Build.0 = Release|Any CPU + {5E08693F-5986-4FB5-81FC-45DD17F0FF1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E08693F-5986-4FB5-81FC-45DD17F0FF1C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E08693F-5986-4FB5-81FC-45DD17F0FF1C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E08693F-5986-4FB5-81FC-45DD17F0FF1C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE