diff --git a/Stormpath.SDK.nuspec b/Stormpath.SDK.nuspec index 5dd8f5d8..de65557f 100644 --- a/Stormpath.SDK.nuspec +++ b/Stormpath.SDK.nuspec @@ -2,7 +2,7 @@ Stormpath.SDK - 0.94.1 + 0.94.2 Nate Barbettini Stormpath, Inc. https://github.com/stormpath/stormpath-sdk-csharp/blob/master/LICENSE @@ -14,12 +14,12 @@ Stormpath API Authentication Authorization REST - + - + diff --git a/src/Stormpath.SDK.Abstractions/Account/EmailVerificationStatus.cs b/src/Stormpath.SDK.Abstractions/Account/EmailVerificationStatus.cs new file mode 100644 index 00000000..ccb8be6b --- /dev/null +++ b/src/Stormpath.SDK.Abstractions/Account/EmailVerificationStatus.cs @@ -0,0 +1,68 @@ +// +// Copyright (c) 2016 Stormpath, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using Stormpath.SDK.Shared; + +namespace Stormpath.SDK.Account +{ + /// + /// Represents the various states an Account's email address may be in. + /// + public sealed class EmailVerificationStatus : StringEnumeration + { + /// + /// The account's email has been verified. + /// + public static EmailVerificationStatus Verified = new EmailVerificationStatus("VERIFIED"); + + /// + /// The account's email has not been verified. + /// + public static EmailVerificationStatus Unverified = new EmailVerificationStatus("UNVERIFIED"); + + /// + /// The account's email status is not known. + /// + public static EmailVerificationStatus Unknown = new EmailVerificationStatus("UNKNOWN"); + + private EmailVerificationStatus(string value) + : base(value) + { + } + + /// + /// Parses a string to an . + /// + /// A string containing "verified", "unverified", or "unknown" (matching is case-insensitive). + /// The with the specified name. + /// No match is found. + public static EmailVerificationStatus Parse(string status) + { + switch (status.ToUpper()) + { + case "VERIFIED": + return Verified; + case "UNVERIFIED": + return Unverified; + case "UNKNOWN": + return Unknown; + default: + throw new Exception($"Could not parse email verification status value '{status.ToUpper()}'"); + } + } + } +} \ No newline at end of file diff --git a/src/Stormpath.SDK.Abstractions/Account/IAccount.cs b/src/Stormpath.SDK.Abstractions/Account/IAccount.cs index 93b75996..5e0970e6 100644 --- a/src/Stormpath.SDK.Abstractions/Account/IAccount.cs +++ b/src/Stormpath.SDK.Abstractions/Account/IAccount.cs @@ -91,6 +91,12 @@ public interface IAccount : /// An , or if this account does not need to verify its email address. IEmailVerificationToken EmailVerificationToken { get; } + /// + /// Gets the account's email verification status. + /// + /// The account's email verification status. + EmailVerificationStatus EmailVerificationStatus { get; } + /// /// Gets the account's status. /// diff --git a/src/Stormpath.SDK.Abstractions/project.json b/src/Stormpath.SDK.Abstractions/project.json index 60c03086..155cb322 100644 --- a/src/Stormpath.SDK.Abstractions/project.json +++ b/src/Stormpath.SDK.Abstractions/project.json @@ -36,5 +36,5 @@ "tooling": { "defaultNamespace": "Stormpath.SDK" }, - "version": "0.94.1" + "version": "0.94.2" } diff --git a/src/Stormpath.SDK.Core/Impl/Account/DefaultAccount.cs b/src/Stormpath.SDK.Core/Impl/Account/DefaultAccount.cs index c86cf5b2..188874ea 100644 --- a/src/Stormpath.SDK.Core/Impl/Account/DefaultAccount.cs +++ b/src/Stormpath.SDK.Core/Impl/Account/DefaultAccount.cs @@ -31,6 +31,7 @@ internal sealed partial class DefaultAccount : AbstractExtendableInstanceResourc private static readonly string DirectoryPropertyName = "directory"; private static readonly string EmailPropertyName = "email"; private static readonly string EmailVerificationTokenPropertyName = "emailVerificationToken"; + private static readonly string EmailVerificationStatusPropertyName = "emailVerificationStatus"; private static readonly string FullNamePropertyName = "fullName"; private static readonly string GivenNamePropertyName = "givenName"; private static readonly string GroupMembershipsPropertyName = "groupMemberships"; @@ -65,6 +66,8 @@ public DefaultAccount(ResourceData data) internal IEmbeddedProperty EmailVerificationToken => this.GetLinkProperty(EmailVerificationTokenPropertyName); + EmailVerificationStatus IAccount.EmailVerificationStatus => GetProperty(EmailVerificationStatusPropertyName); + IEmailVerificationToken IAccount.EmailVerificationToken { get diff --git a/src/Stormpath.SDK.Core/Impl/Serialization/FieldConverters/StatusFieldConverters.cs b/src/Stormpath.SDK.Core/Impl/Serialization/FieldConverters/StatusFieldConverters.cs index 0b18337b..6ed98f6e 100644 --- a/src/Stormpath.SDK.Core/Impl/Serialization/FieldConverters/StatusFieldConverters.cs +++ b/src/Stormpath.SDK.Core/Impl/Serialization/FieldConverters/StatusFieldConverters.cs @@ -41,10 +41,40 @@ protected override FieldConverterResult ConvertImpl(KeyValuePair return FieldConverterResult.Failed; } + // Only convert the Account.Status value + if (!token.Key.Equals("status", StringComparison.OrdinalIgnoreCase)) + { + return FieldConverterResult.Failed; + } + return new FieldConverterResult(true, SDK.Account.AccountStatus.Parse(token.Value.ToString())); } } + internal sealed class AccountEmailVerificationStatusConverter : AbstractFieldConverter + { + public AccountEmailVerificationStatusConverter() + : base(nameof(AccountEmailVerificationStatusConverter), typeof(SDK.Account.IAccount)) + { + } + + protected override FieldConverterResult ConvertImpl(KeyValuePair token) + { + if (!IsStatusField(token)) + { + return FieldConverterResult.Failed; + } + + // Only convert the Account.EmailVerificationStatus value + if (!token.Key.Equals("emailVerificationStatus", StringComparison.OrdinalIgnoreCase)) + { + return FieldConverterResult.Failed; + } + + return new FieldConverterResult(true, SDK.Account.EmailVerificationStatus.Parse(token.Value.ToString())); + } + } + internal sealed class ApplicationStatusConverter : AbstractFieldConverter { public ApplicationStatusConverter() @@ -59,6 +89,12 @@ protected override FieldConverterResult ConvertImpl(KeyValuePair return FieldConverterResult.Failed; } + // Only convert the Application.Status value + if (!token.Key.Equals("status", StringComparison.OrdinalIgnoreCase)) + { + return FieldConverterResult.Failed; + } + return new FieldConverterResult(true, SDK.Application.ApplicationStatus.Parse(token.Value.ToString())); } } @@ -77,6 +113,12 @@ protected override FieldConverterResult ConvertImpl(KeyValuePair return FieldConverterResult.Failed; } + // Only convert the Directory.Status value + if (!token.Key.Equals("status", StringComparison.OrdinalIgnoreCase)) + { + return FieldConverterResult.Failed; + } + return new FieldConverterResult(true, SDK.Directory.DirectoryStatus.Parse(token.Value.ToString())); } } @@ -95,6 +137,12 @@ protected override FieldConverterResult ConvertImpl(KeyValuePair return FieldConverterResult.Failed; } + // Only convert the Group.Status value + if (!token.Key.Equals("status", StringComparison.OrdinalIgnoreCase)) + { + return FieldConverterResult.Failed; + } + return new FieldConverterResult(true, SDK.Group.GroupStatus.Parse(token.Value.ToString())); } } @@ -113,6 +161,12 @@ protected override FieldConverterResult ConvertImpl(KeyValuePair return FieldConverterResult.Failed; } + // Only convert the Organization.Status value + if (!token.Key.Equals("status", StringComparison.OrdinalIgnoreCase)) + { + return FieldConverterResult.Failed; + } + return new FieldConverterResult(true, SDK.Organization.OrganizationStatus.Parse(token.Value.ToString())); } } @@ -131,6 +185,12 @@ protected override FieldConverterResult ConvertImpl(KeyValuePair return FieldConverterResult.Failed; } + // Only convert the ApiKey.Status value + if (!token.Key.Equals("status", StringComparison.OrdinalIgnoreCase)) + { + return FieldConverterResult.Failed; + } + return new FieldConverterResult(true, SDK.Api.ApiKeyStatus.Parse(token.Value.ToString())); } } @@ -149,6 +209,12 @@ protected override FieldConverterResult ConvertImpl(KeyValuePair return FieldConverterResult.Failed; } + // Only convert the Email.Status value + if (!token.Key.EndsWith("EmailStatus", StringComparison.OrdinalIgnoreCase)) + { + return FieldConverterResult.Failed; + } + return new FieldConverterResult(true, SDK.Mail.EmailStatus.Parse(token.Value.ToString())); } } diff --git a/src/Stormpath.SDK.Core/Impl/Serialization/JsonSerializationProvider.cs b/src/Stormpath.SDK.Core/Impl/Serialization/JsonSerializationProvider.cs index 19ca3f63..ea3e0154 100644 --- a/src/Stormpath.SDK.Core/Impl/Serialization/JsonSerializationProvider.cs +++ b/src/Stormpath.SDK.Core/Impl/Serialization/JsonSerializationProvider.cs @@ -36,6 +36,7 @@ public JsonSerializationProvider(IJsonSerializer externalSerializer) new LinkPropertyConverter(), new ExpandedPropertyConverter(converter: this.ConvertProperties), new StatusFieldConverters.AccountStatusConverter(), + new StatusFieldConverters.AccountEmailVerificationStatusConverter(), new StatusFieldConverters.ApplicationStatusConverter(), new StatusFieldConverters.DirectoryStatusConverter(), new StatusFieldConverters.GroupStatusConverter(), diff --git a/src/Stormpath.SDK.Core/project.json b/src/Stormpath.SDK.Core/project.json index 21a4d4ef..2597bce7 100644 --- a/src/Stormpath.SDK.Core/project.json +++ b/src/Stormpath.SDK.Core/project.json @@ -12,7 +12,7 @@ "Stormpath.Configuration": "6.0.0", "Stormpath.SDK.Abstractions": { "target": "project", - "version": "0.94.1" + "version": "0.94.2" }, "System.Runtime.InteropServices.RuntimeInformation": "4.0.0" }, @@ -48,5 +48,5 @@ "tooling": { "defaultNamespace": "Stormpath.SDK" }, - "version": "0.94.1" + "version": "0.94.2" } diff --git a/test/Stormpath.SDK.Tests.Common/Fakes/FakeJson.cs b/test/Stormpath.SDK.Tests.Common/Fakes/FakeJson.cs index 45193f0e..dc56de23 100644 --- a/test/Stormpath.SDK.Tests.Common/Fakes/FakeJson.cs +++ b/test/Stormpath.SDK.Tests.Common/Fakes/FakeJson.cs @@ -77,6 +77,7 @@ public static class FakeJson }, ""email"": ""han.solo@corellia.core"", ""emailVerificationToken"": null, + ""emailVerificationStatus"": ""UNKNOWN"", ""fullName"": ""Han Solo"", ""givenName"": ""Han"", ""groupMemberships"": { diff --git a/test/Stormpath.SDK.Tests/DefaultDataStore_tests.cs b/test/Stormpath.SDK.Tests/DefaultDataStore_tests.cs index 269d1098..45ad90be 100644 --- a/test/Stormpath.SDK.Tests/DefaultDataStore_tests.cs +++ b/test/Stormpath.SDK.Tests/DefaultDataStore_tests.cs @@ -273,7 +273,7 @@ public async Task Supports_list_property() { var dataStore = TestDataStore.Create(new StubRequestExecutor(FakeJson.Application).Object); - var application = await dataStore.GetResourceAsync("/account", CancellationToken.None); + var application = await dataStore.GetResourceAsync("/application", CancellationToken.None); // Verify against data from FakeJson.Application application.Name.ShouldBe("Lightsabers Galore"); @@ -283,5 +283,21 @@ public async Task Supports_list_property() application.AuthorizedCallbackUris.ShouldContain("https://foo.bar/1"); application.AuthorizedCallbackUris.ShouldContain("https://foo.bar/2"); } + + /// + /// Regression test for https://github.com/stormpath/stormpath-sdk-dotnet/issues/212 + /// + /// A Task that represents the asynchronous test. + [Fact] + public async Task Supports_email_verification_status_unknown() + { + var dataStore = TestDataStore.Create(new StubRequestExecutor(FakeJson.Account).Object); + + var account = await dataStore.GetResourceAsync("/account", CancellationToken.None); + + // Verify against data from FakeJson.Application + account.EmailVerificationStatus.ShouldBe(EmailVerificationStatus.Unknown); + } + } }