From ad8fc0eadc0036c79be5f64a0eeacf2bcb886a52 Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Fri, 21 Nov 2025 15:09:16 +0100 Subject: [PATCH 01/22] Make support up to net10.0 and drop netstandard1.4 support Modernizes the codebase by upgrading to .NET 10, enabling use of newer language features and APIs. Removes legacy netstandard1.4 support and related conditional logic to simplify maintenance. Improves compatibility with future .NET releases. --- .github/workflows/publish_nuget_package.yml | 2 +- .../SignhostAPIClient.IntegrationTests.csproj | 2 +- .../SignhostAPIClient.Tests.csproj | 2 +- .../Rest/JsonConverters/JsonBaseConverter.cs | 8 ++--- .../JsonVerificationConverter.cs | 33 +++++++++++++++-- .../Rest/JsonConverters/LevelEnumConverter.cs | 20 ++++++++--- .../Rest/SignHostApiClient.cs | 2 ++ .../StreamContentDigestOptionsExtensions.cs | 36 +++++++------------ .../SignhostAPIClient.csproj | 11 ++---- .../System/SerializableAttribute.cs | 9 ----- 10 files changed, 71 insertions(+), 54 deletions(-) delete mode 100644 src/SignhostAPIClient/System/SerializableAttribute.cs diff --git a/.github/workflows/publish_nuget_package.yml b/.github/workflows/publish_nuget_package.yml index 4dd76bc8..94a5592b 100644 --- a/.github/workflows/publish_nuget_package.yml +++ b/.github/workflows/publish_nuget_package.yml @@ -15,7 +15,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v5 with: - dotnet-version: '8.x' + dotnet-version: '10.x' - name: Check for Tag on Current Commit run: | diff --git a/src/SignhostAPIClient.IntegrationTests/SignhostAPIClient.IntegrationTests.csproj b/src/SignhostAPIClient.IntegrationTests/SignhostAPIClient.IntegrationTests.csproj index 248ede86..c078ecf8 100644 --- a/src/SignhostAPIClient.IntegrationTests/SignhostAPIClient.IntegrationTests.csproj +++ b/src/SignhostAPIClient.IntegrationTests/SignhostAPIClient.IntegrationTests.csproj @@ -1,6 +1,6 @@ - net8.0 + net10.0 false true ../signhost.ruleset diff --git a/src/SignhostAPIClient.Tests/SignhostAPIClient.Tests.csproj b/src/SignhostAPIClient.Tests/SignhostAPIClient.Tests.csproj index cc06f7b3..fbe586e5 100644 --- a/src/SignhostAPIClient.Tests/SignhostAPIClient.Tests.csproj +++ b/src/SignhostAPIClient.Tests/SignhostAPIClient.Tests.csproj @@ -1,6 +1,6 @@ - net8.0 + net10.0 ../signhost.ruleset test diff --git a/src/SignhostAPIClient/Rest/JsonConverters/JsonBaseConverter.cs b/src/SignhostAPIClient/Rest/JsonConverters/JsonBaseConverter.cs index cc3f3e53..ff5642bd 100644 --- a/src/SignhostAPIClient/Rest/JsonConverters/JsonBaseConverter.cs +++ b/src/SignhostAPIClient/Rest/JsonConverters/JsonBaseConverter.cs @@ -8,12 +8,12 @@ namespace Signhost.APIClient.Rest.JsonConverters public abstract class JsonBaseConverter : JsonConverter { -#if TYPEINFO public override bool CanConvert(Type objectType) - => typeof(T).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo()); + => typeof(T) +#if TYPEINFO + .GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo()); #else - public override bool CanConvert(Type objectType) - => typeof(T).IsAssignableFrom(objectType); + .IsAssignableFrom(objectType); #endif public override object ReadJson( diff --git a/src/SignhostAPIClient/Rest/JsonConverters/JsonVerificationConverter.cs b/src/SignhostAPIClient/Rest/JsonConverters/JsonVerificationConverter.cs index 3019809e..4e1f9fa6 100644 --- a/src/SignhostAPIClient/Rest/JsonConverters/JsonVerificationConverter.cs +++ b/src/SignhostAPIClient/Rest/JsonConverters/JsonVerificationConverter.cs @@ -11,7 +11,11 @@ namespace Signhost.APIClient.Rest.JsonConverters internal class JsonVerificationConverter : JsonBaseConverter { +#if TYPEINFO private static readonly IDictionary VerificationTypes = +#else + private static readonly IDictionary VerificationTypes = +#endif CreateVerificationTypeMap(); public override bool CanWrite @@ -30,7 +34,12 @@ internal static void RegisterVerification() { var verification = (IVerification)Activator.CreateInstance(typeof(T)); - VerificationTypes[verification.Type] = typeof(T).GetTypeInfo(); + VerificationTypes[verification.Type] = +#if TYPEINFO + typeof(T).GetTypeInfo(); +#else + typeof(T); +#endif } protected override IVerification Create( @@ -40,13 +49,18 @@ protected override IVerification Create( var typeName = jsonObject["Type"]?.ToString(); if (VerificationTypes.TryGetValue(typeName, out var verificationType)) { +#if TYPEINFO return (IVerification)Activator.CreateInstance(verificationType.AsType()); +#else + return (IVerification)Activator.CreateInstance(verificationType); +#endif } return new UnknownVerification(); } - private static IDictionary CreateVerificationTypeMap() +#if TYPEINFO + private static Dictionary CreateVerificationTypeMap() { return typeof(JsonVerificationConverter).GetTypeInfo().Assembly.ExportedTypes .Select(t => t.GetTypeInfo()) @@ -60,5 +74,20 @@ private static IDictionary CreateVerificationTypeMap() .Where(t => t.instance.Type != null) .ToDictionary(t => t.instance.Type, t => t.typeInfo); } +#else + private static Dictionary CreateVerificationTypeMap() + { + return typeof(JsonVerificationConverter).Assembly.ExportedTypes + .Where(t => typeof(IVerification).IsAssignableFrom(t)) + .Where(t => !t.IsInterface && !t.IsAbstract) +#pragma warning disable SA1008 // Opening parenthesis must be spaced correctly + .Select(t => ( + type: t, + instance: (IVerification)Activator.CreateInstance(t))) +#pragma warning restore SA1008 // Opening parenthesis must be spaced correctly + .Where(t => t.instance.Type is not null) + .ToDictionary(t => t.instance.Type, t => t.type); + } +#endif } } diff --git a/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs b/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs index c088635a..e0e5aeed 100644 --- a/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs +++ b/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs @@ -47,11 +47,23 @@ public override void WriteJson( => throw new NotImplementedException(); private static Type GetUnderlyingType(Type type) - => type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) - ? Nullable.GetUnderlyingType(type) - : type; + => +#if TYPEINFO + type.GetTypeInfo().IsGenericType && +#else + type.IsGenericType && +#endif + type.GetGenericTypeDefinition() == typeof(Nullable<>) + ? Nullable.GetUnderlyingType(type) + : type; private static bool IsLevelEnum(Type type) - => type.GetTypeInfo().IsEnum && type == typeof(Level); + => +#if TYPEINFO + type.GetTypeInfo().IsEnum && +#else + type.IsEnum && +#endif + type == typeof(Level); } } diff --git a/src/SignhostAPIClient/Rest/SignHostApiClient.cs b/src/SignhostAPIClient/Rest/SignHostApiClient.cs index 093632b8..dd2e3a89 100644 --- a/src/SignhostAPIClient/Rest/SignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/SignHostApiClient.cs @@ -22,7 +22,9 @@ public class SignHostApiClient private const string ApiVersion = "v1"; private static readonly string Version = typeof(SignHostApiClient) +#if TYPEINFO .GetTypeInfo() +#endif .Assembly.GetCustomAttribute() .Version; diff --git a/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs b/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs index 2aad9008..3ae2389b 100644 --- a/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs +++ b/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs @@ -63,27 +63,17 @@ private static HashAlgorithm HashAlgorithmCreate( string algorithmName = options.DigestHashAlgorithm; HashAlgorithm algorithm = null; -#if NETSTANDARD1_4 || NETSTANDARD2_0 - switch (algorithmName) { - case "SHA1": - case "SHA-1": - algorithm = SHA1.Create(); - break; - case "SHA256": - case "SHA-256": - algorithm = SHA256.Create(); - break; - case "SHA384": - case "SHA-384": - algorithm = SHA384.Create(); - break; - case "SHA512": - case "SHA-512": - algorithm = SHA512.Create(); - break; - } -#else +#if NET462 algorithm = HashAlgorithm.Create(algorithmName); +#else + algorithm = algorithmName switch { + "SHA1" or "SHA-1" => SHA1.Create(), + "SHA256" or "SHA-256" => SHA256.Create(), + "SHA384" or "SHA-384" => SHA384.Create(), + "SHA512" or "SHA-512" => SHA512.Create(), + + _ => null, + }; #endif if (algorithm == null && options.DigestHashValue == null) { algorithm = DefaultHashAlgorithm(); @@ -98,10 +88,10 @@ private static HashAlgorithm HashAlgorithmCreate( } private static HashAlgorithm DefaultHashAlgorithm() => -#if NETSTANDARD1_4 || NETSTANDARD2_0 - SHA256.Create(); -#else +#if NET462 HashAlgorithm.Create(); +#else + SHA256.Create(); #endif } } diff --git a/src/SignhostAPIClient/SignhostAPIClient.csproj b/src/SignhostAPIClient/SignhostAPIClient.csproj index f8acfff0..8c6a83a0 100644 --- a/src/SignhostAPIClient/SignhostAPIClient.csproj +++ b/src/SignhostAPIClient/SignhostAPIClient.csproj @@ -1,11 +1,9 @@ - netstandard2.0;netstandard1.4;net462 + net10.0;net9.0;net8.0;netstandard2.0;net462 + 10 ../signhost.ruleset - SERIALIZABLE - TYPEINFO TYPEINFO - SERIALIZABLE Signhost.APIClient @@ -53,9 +51,4 @@ - - - - - diff --git a/src/SignhostAPIClient/System/SerializableAttribute.cs b/src/SignhostAPIClient/System/SerializableAttribute.cs deleted file mode 100644 index 568c0bad..00000000 --- a/src/SignhostAPIClient/System/SerializableAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace System -{ -#if !SERIALIZABLE - internal sealed class SerializableAttribute - : Attribute - { - } -#endif -} From 093db3eb3ce96354b6300b744bb6dab9f268d995 Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Fri, 21 Nov 2025 18:53:41 +0100 Subject: [PATCH 02/22] Refactor Signhost API Client to use System.Text.Json - Replaced Newtonsoft.Json with System.Text.Json for JSON serialization and deserialization across the API client. - Updated interfaces and classes to reflect consistent naming conventions (e.g., ISignHostApiClient to ISignhostApiClient). - Removed obsolete JsonConverter classes that were specific to Newtonsoft.Json. - Introduced centralized JSON serializer options in SignhostJsonSerializerOptions for consistent serialization behavior. - Added new JSON test resources for various transaction scenarios to ensure compatibility with the updated serialization logic. --- README.md | 4 +- .../SignhostAPIClient.IntegrationTests.csproj | 1 - .../TransactionTests.cs | 8 +- .../APIResponses.Designer.cs | 186 -------- src/SignhostAPIClient.Tests/APIResponses.resx | 443 ------------------ .../AddOrReplaceFileMetaToTransaction.json | 1 + .../JSON/AddTransaction.json | 80 ++++ .../JSON/DeleteTransaction.json | 80 ++++ .../JSON/GetTransaction.json | 80 ++++ .../JSON/JsonResources.cs | 37 ++ .../JSON/MinimalTransactionResponse.json | 4 + .../JSON/MockPostbackInvalid.json | 76 +++ .../JSON/MockPostbackValid.json | 112 +++++ .../JSON/TransactionSingleSignerJson.json | 46 ++ .../LevelEnumConverterTests.cs | 10 +- src/SignhostAPIClient.Tests/PostbackTests.cs | 12 +- .../RequestBodies.Designer.cs | 114 ----- .../RequestBodies.resx | 355 -------------- .../SignhostAPIClient.Tests.csproj | 21 +- .../SignhostApiClientTests.cs | 166 +++---- .../SignhostApiReceiverTests.cs | 9 +- src/SignhostAPIClient.Tests/app.config | 11 - .../Rest/DataObjects/Activity.cs | 4 +- .../Rest/DataObjects/ConsentVerification.cs | 4 - .../Rest/DataObjects/CscVerification.cs | 5 - .../Rest/DataObjects/DigidVerification.cs | 2 - .../DataObjects/EherkenningVerification.cs | 5 - .../DataObjects/EidasLoginVerification.cs | 8 +- .../Rest/DataObjects/FileMeta.cs | 4 +- .../Rest/DataObjects/IPAddressVerification.cs | 15 - .../Rest/DataObjects/IVerification.cs | 20 +- .../Rest/DataObjects/IdealVerification.cs | 2 - .../Rest/DataObjects/IdinVerification.cs | 2 - .../ItsmeIdentificationVerification.cs | 5 - .../Rest/DataObjects/ItsmeSignVerification.cs | 14 - .../Rest/DataObjects/KennisnetVerification.cs | 11 - .../Rest/DataObjects/Level.cs | 4 - .../Rest/DataObjects/OidcVerification.cs | 5 - .../Rest/DataObjects/OnfidoVerification.cs | 5 - .../DataObjects/PhoneNumberVerification.cs | 2 - .../Rest/DataObjects/PostbackTransaction.cs | 18 +- .../Rest/DataObjects/Receiver.cs | 4 +- .../Rest/DataObjects/ScribbleVerification.cs | 2 - .../Rest/DataObjects/Signer.cs | 2 +- .../SigningCertificateVerification.cs | 13 - .../Rest/DataObjects/SurfnetVerification.cs | 2 - .../Rest/DataObjects/Transaction.cs | 34 +- .../Rest/DataObjects/UnknownVerification.cs | 8 - ...pResponseMessageErrorHandlingExtensions.cs | 23 +- .../Rest/HttpContentJsonExtensions.cs | 4 +- .../Rest/ISignHostApiClient.cs | 2 +- .../Rest/ISignhostApiClientSettings.cs | 2 +- src/SignhostAPIClient/Rest/JsonContent.cs | 4 +- .../Rest/JsonConverters/JsonBaseConverter.cs | 33 -- .../JsonVerificationConverter.cs | 93 ---- .../Rest/JsonConverters/LevelEnumConverter.cs | 81 ++-- .../Rest/SignHostApiClient.cs | 38 +- .../Rest/SignHostApiClientSettings.cs | 8 +- .../Rest/SignhostApiReceiver.cs | 12 +- .../Rest/SignhostJsonSerializerOptions.cs | 23 + .../SignhostAPIClient.csproj | 2 +- 61 files changed, 764 insertions(+), 1617 deletions(-) delete mode 100644 src/SignhostAPIClient.Tests/APIResponses.Designer.cs delete mode 100644 src/SignhostAPIClient.Tests/APIResponses.resx create mode 100644 src/SignhostAPIClient.Tests/JSON/AddOrReplaceFileMetaToTransaction.json create mode 100644 src/SignhostAPIClient.Tests/JSON/AddTransaction.json create mode 100644 src/SignhostAPIClient.Tests/JSON/DeleteTransaction.json create mode 100644 src/SignhostAPIClient.Tests/JSON/GetTransaction.json create mode 100644 src/SignhostAPIClient.Tests/JSON/JsonResources.cs create mode 100644 src/SignhostAPIClient.Tests/JSON/MinimalTransactionResponse.json create mode 100644 src/SignhostAPIClient.Tests/JSON/MockPostbackInvalid.json create mode 100644 src/SignhostAPIClient.Tests/JSON/MockPostbackValid.json create mode 100644 src/SignhostAPIClient.Tests/JSON/TransactionSingleSignerJson.json delete mode 100644 src/SignhostAPIClient.Tests/RequestBodies.Designer.cs delete mode 100644 src/SignhostAPIClient.Tests/RequestBodies.resx delete mode 100644 src/SignhostAPIClient.Tests/app.config delete mode 100644 src/SignhostAPIClient/Rest/DataObjects/ItsmeSignVerification.cs delete mode 100644 src/SignhostAPIClient/Rest/DataObjects/KennisnetVerification.cs delete mode 100644 src/SignhostAPIClient/Rest/DataObjects/SigningCertificateVerification.cs delete mode 100644 src/SignhostAPIClient/Rest/DataObjects/UnknownVerification.cs delete mode 100644 src/SignhostAPIClient/Rest/JsonConverters/JsonBaseConverter.cs delete mode 100644 src/SignhostAPIClient/Rest/JsonConverters/JsonVerificationConverter.cs create mode 100644 src/SignhostAPIClient/Rest/SignhostJsonSerializerOptions.cs diff --git a/README.md b/README.md index 6da273b1..16fa8783 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,11 @@ For full API documentation, please visit [evidos.github.io](https://evidos.githu ### Example code The following code is an example of how to create and start a sign transaction with two documents. ```c# -var settings = new SignHostApiClientSettings( +var settings = new SignhostApiClientSettings( "AppName appkey", "apikey or usertoken")); -var client = new SignHostApiClient(settings); +var client = new SignhostApiClient(settings); var transaction = await client.CreateTransactionAsync(new Transaction { Signers = new List { diff --git a/src/SignhostAPIClient.IntegrationTests/SignhostAPIClient.IntegrationTests.csproj b/src/SignhostAPIClient.IntegrationTests/SignhostAPIClient.IntegrationTests.csproj index c078ecf8..04c3ec1c 100644 --- a/src/SignhostAPIClient.IntegrationTests/SignhostAPIClient.IntegrationTests.csproj +++ b/src/SignhostAPIClient.IntegrationTests/SignhostAPIClient.IntegrationTests.csproj @@ -12,7 +12,6 @@ - diff --git a/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs b/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs index e395c875..0e3f6303 100644 --- a/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs +++ b/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs @@ -10,7 +10,7 @@ namespace Signhost.APIClient.Rest.IntegrationTests; public class TransactionTests : IDisposable { - private readonly SignHostApiClient client; + private readonly SignhostApiClient client; private readonly TestConfiguration config; public TransactionTests() @@ -22,11 +22,11 @@ public TransactionTests() "Integration tests are not configured"); } - var settings = new SignHostApiClientSettings(config.AppKey, config.UserToken) { + var settings = new SignhostApiClientSettings(config.AppKey, config.UserToken) { Endpoint = config.ApiBaseUrl }; - client = new SignHostApiClient(settings); + client = new SignhostApiClient(settings); } [Fact] @@ -123,7 +123,7 @@ public async Task Given_complex_transaction_When_created_and_started_Then_all_pr createdTransaction.Language.Should().Be("en-US"); createdTransaction.CreatedDateTime.Should().HaveValue(); createdTransaction.CreatedDateTime.Should().BeCloseTo(DateTimeOffset.UtcNow, TimeSpan.FromMinutes(1)); - createdTransaction.CancelledDateTime.Should().BeNull(); + createdTransaction.CanceledDateTime.Should().BeNull(); createdTransaction.CancellationReason.Should().BeNull(); // Assert - Context diff --git a/src/SignhostAPIClient.Tests/APIResponses.Designer.cs b/src/SignhostAPIClient.Tests/APIResponses.Designer.cs deleted file mode 100644 index 9a092696..00000000 --- a/src/SignhostAPIClient.Tests/APIResponses.Designer.cs +++ /dev/null @@ -1,186 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Signhost.APIClient.Rest.Tests { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class APIResponses { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal APIResponses() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Signhost.APIClient.Rest.Tests.APIResponses", typeof(APIResponses).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to { - /// "Id": "c487be92-0255-40c7-bd7d-20805a65e7d9", - /// "Status": 20, - /// "File": { - /// "Id": "d4bba0df-f9e5-44c8-89db-f2bb46632d7b", - /// "Name": "contract.pdf" - /// }, - /// "Seal": true, - /// "Signers": [ - /// { - /// "Id": "a2932c07-ca93-4011-96f5-a77d2cd1ec32", - /// "Expires": null, - /// "Email": "user@example.com", - /// "Mobile": "+31612345678", - /// "Iban": null, - /// "BSN": null, - /// "RequireScribbleName": false, - /// "RequireScribble": true, - /// "RequireEmailVerification": true, - /// "R [rest of string was truncated]";. - /// - internal static string AddTransaction { - get { - return ResourceManager.GetString("AddTransaction", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to { - /// "Id": "496bec4d-4ac7-428f-894a-b3e9bade725d", - /// "Status": 20, - /// "File": { - /// "Id": "3149bf06-d01e-4f0d-9aa1-77e100e19772", - /// "Name": "contract.pdf" - /// }, - /// "Seal": true, - /// "Signers": [ - /// { - /// "Id": "4813e178-68a4-4105-b007-5ce9a3630867", - /// "Expires": null, - /// "Email": "user@example.com", - /// "Mobile": "+31612345678", - /// "Iban": null, - /// "BSN": null, - /// "RequireScribbleName": false, - /// "RequireScribble": true, - /// "RequireEmailVerification": true, - /// "R [rest of string was truncated]";. - /// - internal static string DeleteTransaction { - get { - return ResourceManager.GetString("DeleteTransaction", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to { - /// "Id": "c487be92-0255-40c7-bd7d-20805a65e7d9", - /// "Status": 20, - /// "File": { - /// "Id": "d4bba0df-f9e5-44c8-89db-f2bb46632d7b", - /// "Name": "contract.pdf" - /// }, - /// "Seal": true, - /// "Signers": [ - /// { - /// "Id": "a2932c07-ca93-4011-96f5-a77d2cd1ec32", - /// "Expires": null, - /// "Email": "user@example.com", - /// "Mobile": "+31612345678", - /// "Iban": null, - /// "BSN": null, - /// "RequireScribbleName": false, - /// "RequireScribble": true, - /// "RequireEmailVerification": true, - /// "R [rest of string was truncated]";. - /// - internal static string GetTransaction { - get { - return ResourceManager.GetString("GetTransaction", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to { - /// "Id": "c487be92-0255-40c7-bd7d-20805a65e7d9", - /// "Status": 20, - /// "Seal": true, - /// "Signers": [ - /// { - /// "Id": "a2932c07-ca93-4011-96f5-a77d2cd1ec32", - /// "Expires": null, - /// "Email": "user@example.com", - /// "Mobile": "+31612345678", - /// "Iban": null, - /// "BSN": null, - /// "SendSignRequest": true, - /// "SendSignConfirmation": null, - /// "SignRequestMessage": "Hello, could you please sign this document? Best regards, John Doe", - /// "DaysToRemind": 15, - /// "Language": "en-US", - /// "Reference": "Client #123", - /// "ReturnUrl": "h [rest of string was truncated]";. - /// - internal static string GetTransactionCustomVerificationType { - get { - return ResourceManager.GetString("GetTransactionCustomVerificationType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to { - /// "Id": "50262c3f-9744-45bf-a4c6-8a3whatever", - /// "Status": 5 - ///}. - /// - internal static string MinimalTransactionResponse { - get { - return ResourceManager.GetString("MinimalTransactionResponse", resourceCulture); - } - } - } -} diff --git a/src/SignhostAPIClient.Tests/APIResponses.resx b/src/SignhostAPIClient.Tests/APIResponses.resx deleted file mode 100644 index 197489d0..00000000 --- a/src/SignhostAPIClient.Tests/APIResponses.resx +++ /dev/null @@ -1,443 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - { - "Id": "c487be92-0255-40c7-bd7d-20805a65e7d9", - "Status": 20, - "File": { - "Id": "d4bba0df-f9e5-44c8-89db-f2bb46632d7b", - "Name": "contract.pdf" - }, - "Seal": true, - "Signers": [ - { - "Id": "a2932c07-ca93-4011-96f5-a77d2cd1ec32", - "Expires": null, - "Email": "user@example.com", - "Mobile": "+31612345678", - "Iban": null, - "BSN": null, - "RequireScribbleName": false, - "RequireScribble": true, - "RequireEmailVerification": true, - "RequireSmsVerification": true, - "RequireIdealVerification": false, - "RequireDigidVerification": false, - "RequireKennisnetVerification": false, - "RequireSurfnetVerification": false, - "SendSignRequest": true, - "SendSignConfirmation": null, - "SignRequestMessage": "Hello, could you please sign this document? Best regards, John Doe", - "DaysToRemind": 15, - "Language": "en-US", - "ScribbleName": "John Doe", - "ScribbleNameFixed": false, - "Reference": "Client #123", - "ReturnUrl": "http://signhost.com", - "Activities": [ - { - "Id": "0b47eb5c-e800-4fe3-9795-09d380dff1f9", - "Code": 103, - "Activity": "Opened", - "CreatedDateTime": "2016-03-17T21:08:55.1349315+01:00" - }, - { - "Id": "761f8678-116c-4e86-a47a-c8312681d285", - "Code": 203, - "Activity": "Signed", - "CreatedDateTime": "2016-03-17T21:13:55.1349315+01:00" - } - ], - "RejectReason": null, - "SignUrl": "http://ui.signhost.com/sign/93dc596f-ab81-4d31-87aa-50352c4c237e", - "SignedDateTime": null, - "RejectDateTime": null, - "CreatedDateTime": "2016-03-17T21:08:55.1349315+01:00", - "ModifiedDateTime": "2016-03-17T21:08:55.1349315+01:00", - "Context": null - } - ], - "Receivers": [ - { - "Id": "2fe3dddf-4b50-49d1-a3d2-45b7d175fb97", - "Name": "John Doe", - "Email": "user@example.com", - "Language": "en-US", - "Message": "Hello, please find enclosed the digital signed document. Best regards, John Doe", - "Reference": null, - "Activities": null, - "CreatedDateTime": "2016-03-17T21:08:55.1349315+01:00", - "ModifiedDateTime": "2016-03-17T21:08:55.1349315+01:00", - "Context": null - } - ], - "Reference": "Contract #123", - "PostbackUrl": "http://example.com/postback.php", - "SignRequestMode": 2, - "DaysToExpire": 30, - "SendEmailNotifications": true, - "CreatedDateTime": "2016-03-17T21:08:55.1349315+01:00", - "ModifiedDateTime": "2016-03-17T21:08:55.1349315+01:00", - "CanceledDateTime": null, - "Context": null -} - - - { - "Id": "496bec4d-4ac7-428f-894a-b3e9bade725d", - "Status": 20, - "File": { - "Id": "3149bf06-d01e-4f0d-9aa1-77e100e19772", - "Name": "contract.pdf" - }, - "Seal": true, - "Signers": [ - { - "Id": "4813e178-68a4-4105-b007-5ce9a3630867", - "Expires": null, - "Email": "user@example.com", - "Mobile": "+31612345678", - "Iban": null, - "BSN": null, - "RequireScribbleName": false, - "RequireScribble": true, - "RequireEmailVerification": true, - "RequireSmsVerification": true, - "RequireIdealVerification": false, - "RequireDigidVerification": false, - "RequireKennisnetVerification": false, - "RequireSurfnetVerification": false, - "SendSignRequest": true, - "SendSignConfirmation": null, - "SignRequestMessage": "Hello, could you please sign this document? Best regards, John Doe", - "DaysToRemind": 15, - "Language": "en-US", - "ScribbleName": "John Doe", - "ScribbleNameFixed": false, - "Reference": "Client #123", - "ReturnUrl": "http://signhost.com", - "Activities": [ - { - "Id": "866183ae-0a3c-4441-a589-6fe3e1a0f1a1", - "Code": 103, - "Activity": "Opened", - "CreatedDateTime": "2016-03-31T21:11:42.0267461+02:00" - }, - { - "Id": "9b7c4de9-6b8d-4d22-9b88-b7d2d0f084b9", - "Code": 203, - "Activity": "Signed", - "CreatedDateTime": "2016-03-31T21:16:42.0267461+02:00" - } - ], - "RejectReason": null, - "SignUrl": "http://ui.signhost.com/sign/2eeaa5b9-9d4d-4418-b79f-4a33810e7147", - "SignedDateTime": null, - "RejectDateTime": null, - "CreatedDateTime": "2016-03-31T21:11:42.0267461+02:00", - "ModifiedDateTime": "2016-03-31T21:11:42.0267461+02:00", - "Context": null - } - ], - "Receivers": [ - { - "Id": "af07aaec-b612-4f7c-bb1b-c32603c9c6a2", - "Name": "John Doe", - "Email": "user@example.com", - "Language": "en-US", - "Message": "Hello, please find enclosed the digital signed document. Best regards, John Doe", - "Reference": null, - "Activities": null, - "CreatedDateTime": "2016-03-31T21:11:42.0267461+02:00", - "ModifiedDateTime": "2016-03-31T21:11:42.0267461+02:00", - "Context": null - } - ], - "Reference": "Contract #123", - "PostbackUrl": "http://example.com/postback.php", - "SignRequestMode": 2, - "DaysToExpire": 30, - "SendEmailNotifications": true, - "CreatedDateTime": "2016-03-31T21:11:42.0267461+02:00", - "ModifiedDateTime": "2016-03-31T21:11:42.0267461+02:00", - "CanceledDateTime": null, - "Context": null -} - - - { - "Id": "c487be92-0255-40c7-bd7d-20805a65e7d9", - "Status": 20, - "File": { - "Id": "d4bba0df-f9e5-44c8-89db-f2bb46632d7b", - "Name": "contract.pdf" - }, - "Seal": true, - "Signers": [ - { - "Id": "a2932c07-ca93-4011-96f5-a77d2cd1ec32", - "Expires": null, - "Email": "user@example.com", - "Mobile": "+31612345678", - "Iban": null, - "BSN": null, - "RequireScribbleName": false, - "RequireScribble": true, - "RequireEmailVerification": true, - "RequireSmsVerification": true, - "RequireIdealVerification": false, - "RequireDigidVerification": false, - "RequireKennisnetVerification": false, - "RequireSurfnetVerification": false, - "SendSignRequest": true, - "SendSignConfirmation": null, - "SignRequestMessage": "Hello, could you please sign this document? Best regards, John Doe", - "DaysToRemind": 15, - "Language": "en-US", - "ScribbleName": "John Doe", - "ScribbleNameFixed": false, - "Reference": "Client #123", - "ReturnUrl": "http://signhost.com", - "Activities": [ - { - "Id": "0b47eb5c-e800-4fe3-9795-09d380dff1f9", - "Code": 103, - "Activity": "Opened", - "CreatedDateTime": "2016-03-17T21:08:55.1349315+01:00" - }, - { - "Id": "761f8678-116c-4e86-a47a-c8312681d285", - "Code": 203, - "Activity": "Signed", - "CreatedDateTime": "2016-03-17T21:13:55.1349315+01:00" - } - ], - "RejectReason": null, - "SignUrl": "http://ui.signhost.com/sign/93dc596f-ab81-4d31-87aa-50352c4c237e", - "SignedDateTime": null, - "RejectDateTime": null, - "CreatedDateTime": "2016-03-17T21:08:55.1349315+01:00", - "ModifiedDateTime": "2016-03-17T21:08:55.1349315+01:00", - "Context": null - } - ], - "Receivers": [ - { - "Id": "2fe3dddf-4b50-49d1-a3d2-45b7d175fb97", - "Name": "John Doe", - "Email": "user@example.com", - "Language": "en-US", - "Message": "Hello, please find enclosed the digital signed document. Best regards, John Doe", - "Reference": null, - "Activities": null, - "CreatedDateTime": "2016-03-17T21:08:55.1349315+01:00", - "ModifiedDateTime": "2016-03-17T21:08:55.1349315+01:00", - "Context": null - } - ], - "Reference": "Contract #123", - "PostbackUrl": "http://example.com/postback.php", - "SignRequestMode": 2, - "DaysToExpire": 30, - "SendEmailNotifications": true, - "CreatedDateTime": "2016-03-17T21:08:55.1349315+01:00", - "ModifiedDateTime": "2016-03-17T21:08:55.1349315+01:00", - "CanceledDateTime": null, - "Context": null -} - - - { - "Id": "c487be92-0255-40c7-bd7d-20805a65e7d9", - "Status": 20, - "Seal": true, - "Signers": [ - { - "Id": "a2932c07-ca93-4011-96f5-a77d2cd1ec32", - "Expires": null, - "Email": "user@example.com", - "Mobile": "+31612345678", - "Iban": null, - "BSN": null, - "SendSignRequest": true, - "SendSignConfirmation": null, - "SignRequestMessage": "Hello, could you please sign this document? Best regards, John Doe", - "DaysToRemind": 15, - "Language": "en-US", - "Reference": "Client #123", - "ReturnUrl": "http://signhost.com", - "Verifications": [ - { - "Type": "CustomVerificationType" - }, - { - "Type": "IPAddress", - "IPAddress": "127.0.0.33" - }, - { - "Type": "PhoneNumber", - "Number": "123" - } - ], - "Activities": [ - { - "Id": "0b47eb5c-e800-4fe3-9795-09d380dff1f9", - "Code": 103, - "Activity": "Opened", - "CreatedDateTime": "2016-03-17T21:08:55.1349315+01:00" - }, - { - "Id": "761f8678-116c-4e86-a47a-c8312681d285", - "Code": 203, - "Activity": "Signed", - "CreatedDateTime": "2016-03-17T21:13:55.1349315+01:00" - } - ], - "SignUrl": "http://ui.signhost.com/sign/93dc596f-ab81-4d31-87aa-50352c4c237e", - "CreatedDateTime": "2016-03-17T21:08:55.1349315+01:00", - "ModifiedDateTime": "2016-03-17T21:08:55.1349315+01:00" - } - ], - "Receivers": [ - { - "Id": "2fe3dddf-4b50-49d1-a3d2-45b7d175fb97", - "Name": "John Doe", - "Email": "user@example.com", - "Language": "en-US", - "Message": "Hello, please find enclosed the digital signed document. Best regards, John Doe", - "CreatedDateTime": "2016-03-17T21:08:55.1349315+01:00", - "ModifiedDateTime": "2016-03-17T21:08:55.1349315+01:00", - } - ], - "Reference": "Contract #123", - "SignRequestMode": 2, - "DaysToExpire": 30, - "SendEmailNotifications": true, - "CreatedDateTime": "2016-03-17T21:08:55.1349315+01:00", - "ModifiedDateTime": "2016-03-17T21:08:55.1349315+01:00" -} - - - { - "Id": "50262c3f-9744-45bf-a4c6-8a3whatever", - "Status": 5 -} - - \ No newline at end of file diff --git a/src/SignhostAPIClient.Tests/JSON/AddOrReplaceFileMetaToTransaction.json b/src/SignhostAPIClient.Tests/JSON/AddOrReplaceFileMetaToTransaction.json new file mode 100644 index 00000000..1fc00000 --- /dev/null +++ b/src/SignhostAPIClient.Tests/JSON/AddOrReplaceFileMetaToTransaction.json @@ -0,0 +1 @@ +{"Signers":{"someSignerId":{"FormSets":["SampleFormSet"]}},"FormSets":{"SampleFormSet":{"SampleCheck":{"Type":"Check","Value":"I agree","Location":{"Search":"test"}}}}} \ No newline at end of file diff --git a/src/SignhostAPIClient.Tests/JSON/AddTransaction.json b/src/SignhostAPIClient.Tests/JSON/AddTransaction.json new file mode 100644 index 00000000..ea0bbe9f --- /dev/null +++ b/src/SignhostAPIClient.Tests/JSON/AddTransaction.json @@ -0,0 +1,80 @@ +{ + "Id":"c487be92-0255-40c7-bd7d-20805a65e7d9", + "Status":20, + "File":{ + "Id":"d4bba0df-f9e5-44c8-89db-f2bb46632d7b", + "Name":"contract.pdf" + }, + "Seal":true, + "Signers":[ + { + "Id":"a2932c07-ca93-4011-96f5-a77d2cd1ec32", + "Expires":null, + "Email":"user@example.com", + "Mobile":"+31612345678", + "Iban":null, + "BSN":null, + "RequireScribbleName":false, + "RequireScribble":true, + "RequireEmailVerification":true, + "RequireSmsVerification":true, + "RequireIdealVerification":false, + "RequireDigidVerification":false, + "RequireKennisnetVerification":false, + "RequireSurfnetVerification":false, + "SendSignRequest":true, + "SendSignConfirmation":null, + "SignRequestMessage":"Hello, could you please sign this document? Best regards, John Doe", + "DaysToRemind":15, + "Language":"en-US", + "ScribbleName":"John Doe", + "ScribbleNameFixed":false, + "Reference":"Client #123", + "ReturnUrl":"http://signhost.com", + "Activities":[ + { + "Id":"0b47eb5c-e800-4fe3-9795-09d380dff1f9", + "Code":103, + "Activity":"Opened", + "CreatedDateTime":"2016-03-17T21:08:55.1349315+01:00" + }, + { + "Id":"761f8678-116c-4e86-a47a-c8312681d285", + "Code":203, + "Activity":"Signed", + "CreatedDateTime":"2016-03-17T21:13:55.1349315+01:00" + } + ], + "RejectReason":null, + "SignUrl":"http://ui.signhost.com/sign/93dc596f-ab81-4d31-87aa-50352c4c237e", + "SignedDateTime":null, + "RejectDateTime":null, + "CreatedDateTime":"2016-03-17T21:08:55.1349315+01:00", + "ModifiedDateTime":"2016-03-17T21:08:55.1349315+01:00", + "Context":null + } + ], + "Receivers":[ + { + "Id":"2fe3dddf-4b50-49d1-a3d2-45b7d175fb97", + "Name":"John Doe", + "Email":"user@example.com", + "Language":"en-US", + "Message":"Hello, please find enclosed the digital signed document. Best regards, John Doe", + "Reference":null, + "Activities":null, + "CreatedDateTime":"2016-03-17T21:08:55.1349315+01:00", + "ModifiedDateTime":"2016-03-17T21:08:55.1349315+01:00", + "Context":null + } + ], + "Reference":"Contract #123", + "PostbackUrl":"http://example.com/postback.php", + "SignRequestMode":2, + "DaysToExpire":30, + "SendEmailNotifications":true, + "CreatedDateTime":"2016-03-17T21:08:55.1349315+01:00", + "ModifiedDateTime":"2016-03-17T21:08:55.1349315+01:00", + "CanceledDateTime":null, + "Context":null +} diff --git a/src/SignhostAPIClient.Tests/JSON/DeleteTransaction.json b/src/SignhostAPIClient.Tests/JSON/DeleteTransaction.json new file mode 100644 index 00000000..d87b4271 --- /dev/null +++ b/src/SignhostAPIClient.Tests/JSON/DeleteTransaction.json @@ -0,0 +1,80 @@ +{ + "Id":"496bec4d-4ac7-428f-894a-b3e9bade725d", + "Status":20, + "File":{ + "Id":"3149bf06-d01e-4f0d-9aa1-77e100e19772", + "Name":"contract.pdf" + }, + "Seal":true, + "Signers":[ + { + "Id":"4813e178-68a4-4105-b007-5ce9a3630867", + "Expires":null, + "Email":"user@example.com", + "Mobile":"+31612345678", + "Iban":null, + "BSN":null, + "RequireScribbleName":false, + "RequireScribble":true, + "RequireEmailVerification":true, + "RequireSmsVerification":true, + "RequireIdealVerification":false, + "RequireDigidVerification":false, + "RequireKennisnetVerification":false, + "RequireSurfnetVerification":false, + "SendSignRequest":true, + "SendSignConfirmation":null, + "SignRequestMessage":"Hello, could you please sign this document? Best regards, John Doe", + "DaysToRemind":15, + "Language":"en-US", + "ScribbleName":"John Doe", + "ScribbleNameFixed":false, + "Reference":"Client #123", + "ReturnUrl":"http://signhost.com", + "Activities":[ + { + "Id":"866183ae-0a3c-4441-a589-6fe3e1a0f1a1", + "Code":103, + "Activity":"Opened", + "CreatedDateTime":"2016-03-31T21:11:42.0267461+02:00" + }, + { + "Id":"9b7c4de9-6b8d-4d22-9b88-b7d2d0f084b9", + "Code":203, + "Activity":"Signed", + "CreatedDateTime":"2016-03-31T21:16:42.0267461+02:00" + } + ], + "RejectReason":null, + "SignUrl":"http://ui.signhost.com/sign/2eeaa5b9-9d4d-4418-b79f-4a33810e7147", + "SignedDateTime":null, + "RejectDateTime":null, + "CreatedDateTime":"2016-03-31T21:11:42.0267461+02:00", + "ModifiedDateTime":"2016-03-31T21:11:42.0267461+02:00", + "Context":null + } + ], + "Receivers":[ + { + "Id":"af07aaec-b612-4f7c-bb1b-c32603c9c6a2", + "Name":"John Doe", + "Email":"user@example.com", + "Language":"en-US", + "Message":"Hello, please find enclosed the digital signed document. Best regards, John Doe", + "Reference":null, + "Activities":null, + "CreatedDateTime":"2016-03-31T21:11:42.0267461+02:00", + "ModifiedDateTime":"2016-03-31T21:11:42.0267461+02:00", + "Context":null + } + ], + "Reference":"Contract #123", + "PostbackUrl":"http://example.com/postback.php", + "SignRequestMode":2, + "DaysToExpire":30, + "SendEmailNotifications":true, + "CreatedDateTime":"2016-03-31T21:11:42.0267461+02:00", + "ModifiedDateTime":"2016-03-31T21:11:42.0267461+02:00", + "CanceledDateTime":null, + "Context":null +} diff --git a/src/SignhostAPIClient.Tests/JSON/GetTransaction.json b/src/SignhostAPIClient.Tests/JSON/GetTransaction.json new file mode 100644 index 00000000..ea0bbe9f --- /dev/null +++ b/src/SignhostAPIClient.Tests/JSON/GetTransaction.json @@ -0,0 +1,80 @@ +{ + "Id":"c487be92-0255-40c7-bd7d-20805a65e7d9", + "Status":20, + "File":{ + "Id":"d4bba0df-f9e5-44c8-89db-f2bb46632d7b", + "Name":"contract.pdf" + }, + "Seal":true, + "Signers":[ + { + "Id":"a2932c07-ca93-4011-96f5-a77d2cd1ec32", + "Expires":null, + "Email":"user@example.com", + "Mobile":"+31612345678", + "Iban":null, + "BSN":null, + "RequireScribbleName":false, + "RequireScribble":true, + "RequireEmailVerification":true, + "RequireSmsVerification":true, + "RequireIdealVerification":false, + "RequireDigidVerification":false, + "RequireKennisnetVerification":false, + "RequireSurfnetVerification":false, + "SendSignRequest":true, + "SendSignConfirmation":null, + "SignRequestMessage":"Hello, could you please sign this document? Best regards, John Doe", + "DaysToRemind":15, + "Language":"en-US", + "ScribbleName":"John Doe", + "ScribbleNameFixed":false, + "Reference":"Client #123", + "ReturnUrl":"http://signhost.com", + "Activities":[ + { + "Id":"0b47eb5c-e800-4fe3-9795-09d380dff1f9", + "Code":103, + "Activity":"Opened", + "CreatedDateTime":"2016-03-17T21:08:55.1349315+01:00" + }, + { + "Id":"761f8678-116c-4e86-a47a-c8312681d285", + "Code":203, + "Activity":"Signed", + "CreatedDateTime":"2016-03-17T21:13:55.1349315+01:00" + } + ], + "RejectReason":null, + "SignUrl":"http://ui.signhost.com/sign/93dc596f-ab81-4d31-87aa-50352c4c237e", + "SignedDateTime":null, + "RejectDateTime":null, + "CreatedDateTime":"2016-03-17T21:08:55.1349315+01:00", + "ModifiedDateTime":"2016-03-17T21:08:55.1349315+01:00", + "Context":null + } + ], + "Receivers":[ + { + "Id":"2fe3dddf-4b50-49d1-a3d2-45b7d175fb97", + "Name":"John Doe", + "Email":"user@example.com", + "Language":"en-US", + "Message":"Hello, please find enclosed the digital signed document. Best regards, John Doe", + "Reference":null, + "Activities":null, + "CreatedDateTime":"2016-03-17T21:08:55.1349315+01:00", + "ModifiedDateTime":"2016-03-17T21:08:55.1349315+01:00", + "Context":null + } + ], + "Reference":"Contract #123", + "PostbackUrl":"http://example.com/postback.php", + "SignRequestMode":2, + "DaysToExpire":30, + "SendEmailNotifications":true, + "CreatedDateTime":"2016-03-17T21:08:55.1349315+01:00", + "ModifiedDateTime":"2016-03-17T21:08:55.1349315+01:00", + "CanceledDateTime":null, + "Context":null +} diff --git a/src/SignhostAPIClient.Tests/JSON/JsonResources.cs b/src/SignhostAPIClient.Tests/JSON/JsonResources.cs new file mode 100644 index 00000000..9bbfbe6e --- /dev/null +++ b/src/SignhostAPIClient.Tests/JSON/JsonResources.cs @@ -0,0 +1,37 @@ +using System.IO; +using System.Reflection; + +namespace SignhostAPIClient.Tests.JSON; + +public static class JsonResources +{ + public static string TransactionSingleSignerJson { get; } = + GetJson("TransactionSingleSignerJson"); + + public static string AddOrReplaceFileMetaToTransaction { get; } = + GetJson("AddOrReplaceFileMetaToTransaction"); + public static string AddTransaction { get; } = + GetJson("AddTransaction"); + public static string DeleteTransaction { get; } = + GetJson("DeleteTransaction"); + public static string GetTransaction { get; } = + GetJson("GetTransaction"); + public static string MinimalTransactionResponse { get; } = + GetJson("MinimalTransactionResponse"); + public static string MockPostbackInvalid { get; } = + GetJson("MockPostbackInvalid"); + public static string MockPostbackValid { get; } = + GetJson("MockPostbackValid"); + + private static string GetJson(string fileName) + { + var assembly = Assembly.GetExecutingAssembly(); + string resourceName = $"Signhost.APIClient.Rest.Tests.JSON.{fileName}.json"; + + using var stream = assembly.GetManifestResourceStream(resourceName) + ?? throw new FileNotFoundException($"File not found: {fileName}"); + + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); + } +} diff --git a/src/SignhostAPIClient.Tests/JSON/MinimalTransactionResponse.json b/src/SignhostAPIClient.Tests/JSON/MinimalTransactionResponse.json new file mode 100644 index 00000000..99016264 --- /dev/null +++ b/src/SignhostAPIClient.Tests/JSON/MinimalTransactionResponse.json @@ -0,0 +1,4 @@ +{ + "Id":"50262c3f-9744-45bf-a4c6-8a3whatever", + "Status":5 +} diff --git a/src/SignhostAPIClient.Tests/JSON/MockPostbackInvalid.json b/src/SignhostAPIClient.Tests/JSON/MockPostbackInvalid.json new file mode 100644 index 00000000..ce1c2c4e --- /dev/null +++ b/src/SignhostAPIClient.Tests/JSON/MockPostbackInvalid.json @@ -0,0 +1,76 @@ +{ + "Id":"b10ae331-af78-4e79-a39e-5b64693b6b68", + "Status":20, + "Seal":true, + "Signers":[ + { + "Id":"fa95495d-6c59-48e0-962a-a4552f8d6b85", + "Expires":null, + "Email":"user@example.com", + "Mobile":"+31612345678", + "Iban":null, + "BSN":null, + "RequireScribbleName":false, + "RequireScribble":true, + "RequireEmailVerification":true, + "RequireSmsVerification":true, + "RequireIdealVerification":false, + "RequireDigidVerification":false, + "RequireKennisnetVerification":false, + "RequireSurfnetVerification":false, + "SendSignRequest":true, + "SendSignConfirmation":null, + "SignRequestMessage":"Hello, could you please sign this document? Best regards, John Doe", + "DaysToRemind":15, + "Language":"en-US", + "ScribbleName":"John Doe", + "ScribbleNameFixed":false, + "Reference":"Client #123", + "ReturnUrl":"https://signhost.com", + "Activities":[ + { + "Id":"bcba44a9-c201-4494-9920-2c1f7baebcf0", + "Code":103, + "Activity":"Opened", + "CreatedDateTime":"2016-06-15T23:33:04.1965465+02:00" + }, + { + "Id":"de94cf6e-e1a3-4c33-93bf-2013b036daaf", + "Code":203, + "Activity":"Signed", + "CreatedDateTime":"2016-06-15T23:38:04.1965465+02:00" + } + ], + "RejectReason":null, + "SignUrl":"https://view.signhost.com/sign/d3c93bd6-f1ce-48e7-8c9c-c2babfdd4034", + "SignedDateTime":null, + "RejectDateTime":null, + "CreatedDateTime":"2016-06-15T23:33:04.1965465+02:00", + "ModifiedDateTime":"2016-06-15T23:33:04.1965465+02:00", + "Context":null + } + ], + "Receivers":[ + { + "Id":"97ed6b54-b6d1-46ed-88c1-79779c3b47b1", + "Name":"John Doe", + "Email":"user@example.com", + "Language":"en-US", + "Message":"Hello, please find enclosed the digital signed document. Best regards, John Doe", + "Reference":null, + "Activities":null, + "CreatedDateTime":"2016-06-15T23:33:04.1965465+02:00", + "ModifiedDateTime":"2016-06-15T23:33:04.1965465+02:00", + "Context":null + } + ], + "Reference":"Contract #123", + "PostbackUrl":"https://example.com/postback.php", + "SignRequestMode":2, + "DaysToExpire":30, + "SendEmailNotifications":true, + "CreatedDateTime":"2016-08-31T21:22:56.2467731+02:00", + "ModifiedDateTime":"2016-08-31T21:22:56.2467731+02:00", + "CanceledDateTime":null, + "Context":null +} diff --git a/src/SignhostAPIClient.Tests/JSON/MockPostbackValid.json b/src/SignhostAPIClient.Tests/JSON/MockPostbackValid.json new file mode 100644 index 00000000..62ab673a --- /dev/null +++ b/src/SignhostAPIClient.Tests/JSON/MockPostbackValid.json @@ -0,0 +1,112 @@ +{ + "Id":"b10ae331-af78-4e79-a39e-5b64693b6b68", + "Status":20, + "Files":{ + "file1":{ + "Links":[ + { + "Rel":"file", + "Type":"application/pdf", + "Link":"https://api.signhost.com/api/transaction/b10ae331-af78-4e79-a39e-5b64693b6b68/file/file1" + } + ], + "DisplayName":"Sample File" + } + }, + "Seal":true, + "Signers":[ + { + "Id":"fa95495d-6c59-48e0-962a-a4552f8d6b85", + "Expires":null, + "Email":"user@example.com", + "Verifications":[ + { + "Type":"PhoneNumber", + "Number":"+31612345678" + }, + { + "Type":"Scribble", + "RequireHandsignature":false, + "ScribbleNameFixed":false, + "ScribbleName":"John Doe" + }, + { + "Type":"IPAddress", + "IPAddress":"1.2.3.4" + } + ], + "Mobile":"+31612345678", + "Iban":null, + "BSN":null, + "RequireScribbleName":false, + "RequireScribble":true, + "RequireEmailVerification":true, + "RequireSmsVerification":true, + "RequireIdealVerification":false, + "RequireDigidVerification":false, + "RequireKennisnetVerification":false, + "RequireSurfnetVerification":false, + "SendSignRequest":true, + "SendSignConfirmation":null, + "SignRequestMessage":"Hello, could you please sign this document? Best regards, John Doe", + "DaysToRemind":15, + "Language":"en-US", + "ScribbleName":"John Doe", + "ScribbleNameFixed":false, + "Reference":"Client #123", + "ReturnUrl":"https://signhost.com", + "Activities":[ + { + "Id":"bcba44a9-c201-4494-9920-2c1f7baebcf0", + "Code":103, + "Activity":"Opened", + "CreatedDateTime":"2016-06-15T23:33:04.1965465+02:00" + }, + { + "Id":"7aacf96a-5c2f-475d-98a5-726e41bfc5d3", + "Code":105, + "Activity":"DocumentOpened", + "Info":"file1", + "CreatedDateTime":"2020-01-30T16:31:05.6679583+01:00" + }, + { + "Id":"de94cf6e-e1a3-4c33-93bf-2013b036daaf", + "Code":203, + "Activity":"Signed", + "CreatedDateTime":"2016-06-15T23:38:04.1965465+02:00" + } + ], + "RejectReason":null, + "SignUrl":"https://view.signhost.com/sign/d3c93bd6-f1ce-48e7-8c9c-c2babfdd4034", + "SignedDateTime":null, + "RejectDateTime":null, + "CreatedDateTime":"2016-06-15T23:33:04.1965465+02:00", + "ModifiedDateTime":"2016-06-15T23:33:04.1965465+02:00", + "Context":null + } + ], + "Receivers":[ + { + "Id":"97ed6b54-b6d1-46ed-88c1-79779c3b47b1", + "Name":"John Doe", + "Email":"user@example.com", + "Language":"en-US", + "Message":"Hello, please find enclosed the digital signed document. Best regards, John Doe", + "Reference":null, + "Activities":null, + "CreatedDateTime":"2016-06-15T23:33:04.1965465+02:00", + "ModifiedDateTime":"2016-06-15T23:33:04.1965465+02:00", + "Context":null + } + ], + "Reference":"Contract #123", + "PostbackUrl":"https://example.com/postback.php", + "SignRequestMode":2, + "DaysToExpire":30, + "SendEmailNotifications":true, + "CreatedDateTime":"2016-08-31T21:22:56.2467731+02:00", + "ModifiedDateTime":"2016-08-31T21:22:56.2467731+02:00", + "CanceledDateTime":null, + "Context":null, + "Checksum":"cdc09eee2ed6df2846dcc193aedfef59f2834f8d" +} diff --git a/src/SignhostAPIClient.Tests/JSON/TransactionSingleSignerJson.json b/src/SignhostAPIClient.Tests/JSON/TransactionSingleSignerJson.json new file mode 100644 index 00000000..07d2fdb9 --- /dev/null +++ b/src/SignhostAPIClient.Tests/JSON/TransactionSingleSignerJson.json @@ -0,0 +1,46 @@ +{ + "Id":"50262c3f-9744-45bf-a4c6-8a3whatever", + "Status":5, + "CanceledDateTime":"2017-01-01T15:00:00.0000000+01:00", + "Files":{}, + "Seal":false, + "Signers":[ + { + "Id":"Signer1", + "Email":"test1@example.com", + "Verifications":[ + { + "Type":"PhoneNumber", + "Number":"+31615123456" + } + ], + "Mobile":"+31615087075", + "SignRequestMessage":"Hello 1st signer", + "Language":"nl-NL", + "Activities":[ + { + "Id":"Activity1", + "Code":103, + "CreatedDateTime":"2017-05-31T22:15:17.6409005+02:00" + }, + { + "Id":"Activity2", + "Code":105, + "Info":"592f2448347cd", + "CreatedDateTime":"2017-05-31T22:15:20.3284659+02:00" + }, + { + "Id":"25dd4131-f1c4-4e4c-a407-c4164cfe4096", + "Code":105, + "Info":"592f244834807", + "CreatedDateTime":"2017-05-31T22:15:24.4379773+02:00" + } + ] + } + ], + "Receivers":[], + "Reference":"Contract #123", + "SignRequestMode":2, + "DaysToExpire":14, + "Context":null +} diff --git a/src/SignhostAPIClient.Tests/LevelEnumConverterTests.cs b/src/SignhostAPIClient.Tests/LevelEnumConverterTests.cs index 67e491d1..534d5484 100644 --- a/src/SignhostAPIClient.Tests/LevelEnumConverterTests.cs +++ b/src/SignhostAPIClient.Tests/LevelEnumConverterTests.cs @@ -1,5 +1,5 @@ using FluentAssertions; -using Newtonsoft.Json; +using System.Text.Json; using Signhost.APIClient.Rest.DataObjects; using System; using System.Collections; @@ -17,7 +17,7 @@ public void when_Level_is_null_should_deserialize_to_null() const string json = "{\"Type\":\"eIDAS Login\",\"Level\":null}"; // Act - var eidasLogin = JsonConvert.DeserializeObject(json); + var eidasLogin = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); // Assert eidasLogin.Level.Should().Be(null); @@ -30,7 +30,7 @@ public void when_Level_is_not_supplied_should_deserialize_to_null() const string json = "{\"Type\":\"eIDAS Login\"}"; // Act - var eidasLogin = JsonConvert.DeserializeObject(json); + var eidasLogin = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); // Assert eidasLogin.Level.Should().Be(null); @@ -43,7 +43,7 @@ public void when_Level_is_unknown_should_deserialize_to_Unknown_Level() const string json = "{\"Type\":\"eIDAS Login\",\"Level\":\"foobar\"}"; // Act - var eidasLogin = JsonConvert.DeserializeObject(json); + var eidasLogin = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); // Assert eidasLogin.Level.Should().Be(Level.Unknown); @@ -57,7 +57,7 @@ public void when_Level_is_valid_should_deserialize_to_correct_value(Level level) string json = $"{{\"Type\":\"eIDAS Login\",\"Level\":\"{level}\"}}"; // Act - var eidasLogin = JsonConvert.DeserializeObject(json); + var eidasLogin = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); // Assert eidasLogin.Level.Should().Be(level); diff --git a/src/SignhostAPIClient.Tests/PostbackTests.cs b/src/SignhostAPIClient.Tests/PostbackTests.cs index a4724723..7a005953 100644 --- a/src/SignhostAPIClient.Tests/PostbackTests.cs +++ b/src/SignhostAPIClient.Tests/PostbackTests.cs @@ -1,8 +1,9 @@ using System; using System.Linq; +using System.Text.Json; using FluentAssertions; -using Newtonsoft.Json; using Signhost.APIClient.Rest.DataObjects; +using SignhostAPIClient.Tests.JSON; using Xunit; namespace Signhost.APIClient.Rest.Tests @@ -12,8 +13,8 @@ public class PostbackTests [Fact] public void PostbackTransaction_should_get_serialized_correctly() { - string json = RequestBodies.MockPostbackValid; - var postbackTransaction = JsonConvert.DeserializeObject(json); + string json = JsonResources.MockPostbackValid; + var postbackTransaction = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); postbackTransaction.Id .Should().Be("b10ae331-af78-4e79-a39e-5b64693b6b68"); postbackTransaction.Status .Should().Be(TransactionStatus.InProgress); @@ -24,7 +25,7 @@ public void PostbackTransaction_should_get_serialized_correctly() postbackTransaction.DaysToExpire .Should().Be(30); postbackTransaction.SendEmailNotifications.Should().BeTrue(); postbackTransaction.CreatedDateTime .Should().Be(DateTimeOffset.Parse("2016-08-31T21:22:56.2467731+02:00")); - postbackTransaction.CancelledDateTime .Should().BeNull(); + postbackTransaction.CanceledDateTime .Should().BeNull(); (postbackTransaction.Context is null) .Should().BeTrue(); postbackTransaction.Checksum .Should().Be("cdc09eee2ed6df2846dcc193aedfef59f2834f8d"); @@ -53,19 +54,16 @@ public void PostbackTransaction_should_get_serialized_correctly() var phoneNumberVerification = verifications[0] as PhoneNumberVerification; phoneNumberVerification .Should().NotBeNull(); - phoneNumberVerification.Type .Should().Be("PhoneNumber"); phoneNumberVerification.Number.Should().Be("+31612345678"); var scribbleVerification = verifications[1] as ScribbleVerification; scribbleVerification .Should().NotBeNull(); - scribbleVerification.Type .Should().Be("Scribble"); scribbleVerification.RequireHandsignature.Should().BeFalse(); scribbleVerification.ScribbleNameFixed .Should().BeFalse(); scribbleVerification.ScribbleName .Should().Be("John Doe"); var ipAddressVerification = verifications[2] as IPAddressVerification; ipAddressVerification .Should().NotBeNull(); - ipAddressVerification.Type .Should().Be("IPAddress"); ipAddressVerification.IPAddress.Should().Be("1.2.3.4"); var activities = signer.Activities; diff --git a/src/SignhostAPIClient.Tests/RequestBodies.Designer.cs b/src/SignhostAPIClient.Tests/RequestBodies.Designer.cs deleted file mode 100644 index 55227f02..00000000 --- a/src/SignhostAPIClient.Tests/RequestBodies.Designer.cs +++ /dev/null @@ -1,114 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Signhost.APIClient.Rest.Tests { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class RequestBodies { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal RequestBodies() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Signhost.APIClient.Rest.Tests.RequestBodies", typeof(RequestBodies).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to {"DisplayOrder":null,"DisplayName":null,"Description":null,"Signers":{"someSignerId":{"FormSets":["SampleFormSet"]}},"FormSets":{"SampleFormSet":{"SampleCheck":{"Type":"Check","Value":"I agree","Location":{"Search":"test","Occurence":null,"Top":null,"Right":null,"Bottom":null,"Left":null,"Width":null,"Height":null,"PageNumber":null}}}}}. - /// - internal static string AddOrReplaceFileMetaToTransaction { - get { - return ResourceManager.GetString("AddOrReplaceFileMetaToTransaction", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to { - /// "Id": "50262c3f-9744-45bf-a4c6-8a3whatever", - /// "Status": 5, - /// "CanceledDateTime": "2017-01-01 15:00", - /// "Files": {}, - /// "Seal": false, - /// "Signers": [ - /// { - /// "Id": "Signer1", - /// "Email": "test1@example.com", - /// "Verifications": [ - /// { - /// "Type": "PhoneNumber", - /// "Number": "+31615123456" - /// } - /// ], - /// "Mobile": "+31615087075", - /// "SignRequestMessage": "Hello 1st signer", - /// "Language": "nl-NL", - /// "Activities": [ - /// { - /// "I [rest of string was truncated]";. - /// - internal static string TransactionSingleSignerJson { - get { - return ResourceManager.GetString("TransactionSingleSignerJson", resourceCulture); - } - } - - internal static string MockPostbackValid { - get { - return ResourceManager.GetString("MockPostbackValid", resourceCulture); - } - } - - internal static string MockPostbackInvalid { - get { - return ResourceManager.GetString("MockPostbackInvalid", resourceCulture); - } - } - } -} diff --git a/src/SignhostAPIClient.Tests/RequestBodies.resx b/src/SignhostAPIClient.Tests/RequestBodies.resx deleted file mode 100644 index 62d2094a..00000000 --- a/src/SignhostAPIClient.Tests/RequestBodies.resx +++ /dev/null @@ -1,355 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - {"DisplayOrder":null,"DisplayName":null,"Description":null,"Signers":{"someSignerId":{"FormSets":["SampleFormSet"]}},"FormSets":{"SampleFormSet":{"SampleCheck":{"Type":"Check","Value":"I agree","Location":{"Search":"test","Occurence":null,"Top":null,"Right":null,"Bottom":null,"Left":null,"Width":null,"Height":null,"PageNumber":null}}}}} - - - { - "Id": "50262c3f-9744-45bf-a4c6-8a3whatever", - "Status": 5, - "CanceledDateTime": "2017-01-01 15:00", - "Files": {}, - "Seal": false, - "Signers": [ - { - "Id": "Signer1", - "Email": "test1@example.com", - "Verifications": [ - { - "Type": "PhoneNumber", - "Number": "+31615123456" - } - ], - "Mobile": "+31615087075", - "SignRequestMessage": "Hello 1st signer", - "Language": "nl-NL", - "Activities": [ - { - "Id": "Activity1", - "Code": 103, - "CreatedDateTime": "2017-05-31T22:15:17.6409005+02:00" - }, - { - "Id": "Activity2", - "Code": 105, - "Info": "592f2448347cd", - "CreatedDateTime": "2017-05-31T22:15:20.3284659+02:00" - }, - { - "Id": "25dd4131-f1c4-4e4c-a407-c4164cfe4096", - "Code": 105, - "Info": "592f244834807", - "CreatedDateTime": "2017-05-31T22:15:24.4379773+02:00" - } - ] - } - ], - "Receivers": [], - "Reference": "Contract #123", - "SignRequestMode": 2, - "DaysToExpire": 14, - "Context": null -} - - - -{ - "Id": "b10ae331-af78-4e79-a39e-5b64693b6b68", - "Status": 20, - "Files": { - "file1": { - "Links": [ - { - "Rel": "file", - "Type": "application/pdf", - "Link": "https://api.signhost.com/api/transaction/b10ae331-af78-4e79-a39e-5b64693b6b68/file/file1" - } - ], - "DisplayName": "Sample File" - } - }, - "Seal": true, - "Signers": - [{ - "Id": "fa95495d-6c59-48e0-962a-a4552f8d6b85", - "Expires": null, - "Email": "user@example.com", - "Verifications": [ - { - "Type": "PhoneNumber", - "Number": "+31612345678" - }, - { - "Type": "Scribble", - "RequireHandsignature": false, - "ScribbleNameFixed": false, - "ScribbleName": "John Doe" - }, - { - "Type": "IPAddress", - "IPAddress": "1.2.3.4" - } - ], - "Mobile": "+31612345678", - "Iban": null, - "BSN": null, - "RequireScribbleName": false, - "RequireScribble": true, - "RequireEmailVerification": true, - "RequireSmsVerification": true, - "RequireIdealVerification": false, - "RequireDigidVerification": false, - "RequireKennisnetVerification": false, - "RequireSurfnetVerification": false, - "SendSignRequest": true, - "SendSignConfirmation": null, - "SignRequestMessage": "Hello, could you please sign this document? Best regards, John Doe", - "DaysToRemind": 15, - "Language": "en-US", - "ScribbleName": "John Doe", - "ScribbleNameFixed": false, - "Reference": "Client #123", - "ReturnUrl": "https://signhost.com", - "Activities": - [{ - "Id": "bcba44a9-c201-4494-9920-2c1f7baebcf0", - "Code": 103, - "Activity": "Opened", - "CreatedDateTime": "2016-06-15T23:33:04.1965465+02:00"}, - { - "Id": "7aacf96a-5c2f-475d-98a5-726e41bfc5d3", - "Code": 105, - "Activity": "DocumentOpened", - "Info": "file1", - "CreatedDateTime": "2020-01-30T16:31:05.6679583+01:00" - }, - { - "Id": "de94cf6e-e1a3-4c33-93bf-2013b036daaf", - "Code": 203,"Activity": "Signed", - "CreatedDateTime": "2016-06-15T23:38:04.1965465+02:00" - }], - "RejectReason": null, - "SignUrl": "https://view.signhost.com/sign/d3c93bd6-f1ce-48e7-8c9c-c2babfdd4034", - "SignedDateTime": null, - "RejectDateTime": null, - "CreatedDateTime": "2016-06-15T23:33:04.1965465+02:00", - "ModifiedDateTime": "2016-06-15T23:33:04.1965465+02:00", - "Context": null}], - "Receivers": - [{ - "Id": "97ed6b54-b6d1-46ed-88c1-79779c3b47b1", - "Name": "John Doe", - "Email": "user@example.com", - "Language": "en-US", - "Message": "Hello, please find enclosed the digital signed document. Best regards, John Doe", - "Reference": null, - "Activities": null, - "CreatedDateTime": "2016-06-15T23:33:04.1965465+02:00", - "ModifiedDateTime": "2016-06-15T23:33:04.1965465+02:00", - "Context": null - }], - "Reference": "Contract #123", - "PostbackUrl": "https://example.com/postback.php", - "SignRequestMode": 2, - "DaysToExpire": 30, - "SendEmailNotifications": true, - "CreatedDateTime": "2016-08-31T21:22:56.2467731+02:00", - "ModifiedDateTime": "2016-08-31T21:22:56.2467731+02:00", - "CanceledDateTime": null, - "Context": null, - "Checksum": "cdc09eee2ed6df2846dcc193aedfef59f2834f8d" -} - - - - -{ - "Id": "b10ae331-af78-4e79-a39e-5b64693b6b68", - "Status": 20, - "Seal": true, - "Signers": - [{ - "Id": "fa95495d-6c59-48e0-962a-a4552f8d6b85", - "Expires": null, - "Email": "user@example.com", - "Mobile": "+31612345678", - "Iban": null, - "BSN": null, - "RequireScribbleName": false, - "RequireScribble": true, - "RequireEmailVerification": true, - "RequireSmsVerification": true, - "RequireIdealVerification": false, - "RequireDigidVerification": false, - "RequireKennisnetVerification": false, - "RequireSurfnetVerification": false, - "SendSignRequest": true, - "SendSignConfirmation": null, - "SignRequestMessage": "Hello, could you please sign this document? Best regards, John Doe", - "DaysToRemind": 15, - "Language": "en-US", - "ScribbleName": "John Doe", - "ScribbleNameFixed": false, - "Reference": "Client #123", - "ReturnUrl": "https://signhost.com", - "Activities": - [{ - "Id": "bcba44a9-c201-4494-9920-2c1f7baebcf0", - "Code": 103, - "Activity": "Opened", - "CreatedDateTime": "2016-06-15T23:33:04.1965465+02:00"}, - { - "Id": "de94cf6e-e1a3-4c33-93bf-2013b036daaf", - "Code": 203,"Activity": "Signed", - "CreatedDateTime": "2016-06-15T23:38:04.1965465+02:00" - }], - "RejectReason": null, - "SignUrl": "https://view.signhost.com/sign/d3c93bd6-f1ce-48e7-8c9c-c2babfdd4034", - "SignedDateTime": null, - "RejectDateTime": null, - "CreatedDateTime": "2016-06-15T23:33:04.1965465+02:00", - "ModifiedDateTime": "2016-06-15T23:33:04.1965465+02:00", - "Context": null}], - "Receivers": - [{ - "Id": "97ed6b54-b6d1-46ed-88c1-79779c3b47b1", - "Name": "John Doe", - "Email": "user@example.com", - "Language": "en-US", - "Message": "Hello, please find enclosed the digital signed document. Best regards, John Doe", - "Reference": null, - "Activities": null, - "CreatedDateTime": "2016-06-15T23:33:04.1965465+02:00", - "ModifiedDateTime": "2016-06-15T23:33:04.1965465+02:00", - "Context": null - }], - "Reference": "Contract #123", - "PostbackUrl": "https://example.com/postback.php", - "SignRequestMode": 2, - "DaysToExpire": 30, - "SendEmailNotifications": true, - "CreatedDateTime": "2016-08-31T21:22:56.2467731+02:00", - "ModifiedDateTime": "2016-08-31T21:22:56.2467731+02:00", - "CanceledDateTime": null, - "Context": null, -} - - - diff --git a/src/SignhostAPIClient.Tests/SignhostAPIClient.Tests.csproj b/src/SignhostAPIClient.Tests/SignhostAPIClient.Tests.csproj index fbe586e5..d7aa909c 100644 --- a/src/SignhostAPIClient.Tests/SignhostAPIClient.Tests.csproj +++ b/src/SignhostAPIClient.Tests/SignhostAPIClient.Tests.csproj @@ -10,7 +10,6 @@ - @@ -22,23 +21,11 @@ - - Signhost.APIClient.Rest.Tests - - - - ResXFileCodeGenerator - - - ResXFileCodeGenerator - RequestBodies.Designer.cs - + - - - RequestBodies.resx - - + + Signhost.APIClient.Rest.Tests + diff --git a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs index 041a40f4..0e5f1e9b 100644 --- a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs +++ b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs @@ -8,16 +8,18 @@ using System.Collections.Generic; using RichardSzalay.MockHttp; using System.Net; +using SignhostAPIClient.Tests.JSON; +using System.Text.Json.Serialization; namespace Signhost.APIClient.Rest.Tests { - public class SignHostApiClientTests + public class SignhostApiClientTests { - private readonly SignHostApiClientSettings settings = new("AppKey", "Usertoken") { + private readonly SignhostApiClientSettings settings = new("AppKey", "Usertoken") { Endpoint = "http://localhost/api/" }; - private readonly SignHostApiClientSettings oauthSettings = new("AppKey") { + private readonly SignhostApiClientSettings oauthSettings = new("AppKey") { Endpoint = "http://localhost/api/" }; @@ -27,11 +29,11 @@ public async Task when_AddOrReplaceFileMetaToTransaction_is_called_then_the_requ var mockHttp = new MockHttpMessageHandler(); mockHttp.Expect(HttpMethod.Put, "http://localhost/api/transaction/transactionId/file/fileId") - .WithContent(RequestBodies.AddOrReplaceFileMetaToTransaction) + .WithContent(JsonResources.AddOrReplaceFileMetaToTransaction) .Respond(HttpStatusCode.OK); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); var fileSignerMeta = new FileSignerMeta { @@ -76,11 +78,11 @@ public async Task when_a_GetTransaction_is_called_then_we_should_have_called_the var mockHttp = new MockHttpMessageHandler(); mockHttp .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.OK, new StringContent(APIResponses.GetTransaction)); + .Respond(HttpStatusCode.OK, new StringContent(JsonResources.GetTransaction)); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); var result = await signhostApiClient.GetTransactionAsync("transaction Id"); result.Id.Should().Be("c487be92-0255-40c7-bd7d-20805a65e7d9"); @@ -95,11 +97,15 @@ public async Task when_GetTransaction_is_called_and_the_authorization_is_bad_the var mockHttp = new MockHttpMessageHandler(); mockHttp .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.Unauthorized, new StringContent("{'message': 'unauthorized' }")); + .Respond(HttpStatusCode.Unauthorized, new StringContent(""" + { + "message": "unauthorized" + } + """)); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); await getTransaction.Should().ThrowAsync(); @@ -114,11 +120,15 @@ public async Task when_GetTransaction_is_called_and_request_is_bad_then_we_shoul var mockHttp = new MockHttpMessageHandler(); mockHttp .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.BadRequest, new StringContent("{ 'message': 'Bad Request' }")); + .Respond(HttpStatusCode.BadRequest, new StringContent(""" + { + "message": "Bad Request" + } + """)); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); await getTransaction.Should().ThrowAsync(); @@ -133,11 +143,15 @@ public async Task when_GetTransaction_is_called_and_credits_have_run_out_then_we var mockHttp = new MockHttpMessageHandler(); mockHttp .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.PaymentRequired, new StringContent("{ 'type': 'https://api.signhost.com/problem/subscription/out-of-credits' }")); + .Respond(HttpStatusCode.PaymentRequired, new StringContent(""" + { + "type": "https://api.signhost.com/problem/subscription/out-of-credits" + } + """)); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); await getTransaction.Should().ThrowAsync(); @@ -152,11 +166,15 @@ public async Task when_GetTransaction_is_called_and_not_found_then_we_should_get var mockHttp = new MockHttpMessageHandler(); mockHttp .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.NotFound, new StringContent("{ 'Message': 'Not Found' }")); + .Respond(HttpStatusCode.NotFound, new StringContent(""" + { + "message": "Not Found" + } + """)); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); @@ -172,11 +190,15 @@ public async Task when_GetTransaction_is_called_and_unkownerror_like_418_occures var mockHttp = new MockHttpMessageHandler(); mockHttp .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond((HttpStatusCode)418, new StringContent("{ 'message': '418 I\\'m a teapot' }")); + .Respond((HttpStatusCode)418, new StringContent(""" + { + "message": "418 I'm a teapot" + } + """)); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); await getTransaction.Should().ThrowAsync() @@ -192,11 +214,15 @@ public async Task when_GetTransaction_is_called_and_there_is_an_InternalServerEr var mockHttp = new MockHttpMessageHandler(); mockHttp .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.InternalServerError, new StringContent("{ 'message': 'Internal Server Error' }")); + .Respond(HttpStatusCode.InternalServerError, new StringContent(""" + { + "message": "Internal Server Error" + } + """)); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); await getTransaction.Should().ThrowAsync(); @@ -211,10 +237,10 @@ public async Task When_GetTransaction_is_called_on_gone_transaction_we_shoud_get var mockHttp = new MockHttpMessageHandler(); mockHttp .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.Gone, new StringContent(APIResponses.GetTransaction)); + .Respond(HttpStatusCode.Gone, new StringContent(JsonResources.GetTransaction)); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); await getTransaction.Should().ThrowAsync>(); @@ -229,10 +255,10 @@ public async Task When_GetTransaction_is_called_and_gone_is_expected_we_should_g var mockHttp = new MockHttpMessageHandler(); mockHttp .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.Gone, new StringContent(APIResponses.GetTransaction)); + .Respond(HttpStatusCode.Gone, new StringContent(JsonResources.GetTransaction)); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); Func getTransaction = () => signhostApiClient.GetTransactionResponseAsync("transaction Id"); await getTransaction.Should().NotThrowAsync(); @@ -247,10 +273,10 @@ public async Task when_a_CreateTransaction_is_called_then_we_should_have_called_ var mockHttp = new MockHttpMessageHandler(); mockHttp .Expect(HttpMethod.Post, "http://localhost/api/transaction") - .Respond(HttpStatusCode.OK, new StringContent(APIResponses.AddTransaction)); + .Respond(HttpStatusCode.OK, new StringContent(JsonResources.AddTransaction)); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); Signer testSigner = new Signer(); testSigner.Email = "firstname.lastname@gmail.com"; @@ -273,12 +299,12 @@ public async Task when_a_CreateTransaction_is_called_we_can_add_custom_http_head .Expect(HttpMethod.Post, "http://localhost/api/transaction") .WithHeaders("X-Forwarded-For", "localhost") .With(matcher => matcher.Headers.UserAgent.ToString().Contains("SignhostClientLibrary")) - .Respond(HttpStatusCode.OK, new StringContent(APIResponses.AddTransaction)); + .Respond(HttpStatusCode.OK, new StringContent(JsonResources.AddTransaction)); using (var httpClient = mockHttp.ToHttpClient()) { settings.AddHeader = (AddHeaders a) => a("X-Forwarded-For", "localhost"); - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); Transaction testTransaction = new Transaction(); @@ -295,10 +321,14 @@ public async Task when_CreateTransaction_is_called_with_invalid_email_then_we_sh mockHttp .Expect(HttpMethod.Post, "http://localhost/api/transaction") .WithHeaders("Content-Type", "application/json") - .Respond(HttpStatusCode.BadRequest, new StringContent(" { 'message': 'Bad Request' }")); + .Respond(HttpStatusCode.BadRequest, new StringContent(""" + { + "message": "Bad Request" + } + """)); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); Signer testSigner = new Signer(); testSigner.Email = "firstname.lastnamegmail.com"; @@ -319,10 +349,14 @@ public async Task when_a_function_is_called_with_a_wrong_endpoint_we_should_get_ var mockHttp = new MockHttpMessageHandler(); mockHttp .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.BadGateway, new StringContent(" { 'Message': 'Bad Gateway' }")); + .Respond(HttpStatusCode.BadGateway, new StringContent(""" + { + "message": "Bad Gateway" + } + """)); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); await getTransaction.Should().ThrowAsync() @@ -338,10 +372,10 @@ public async Task when_a_DeleteTransaction_is_called_then_we_should_have_called_ var mockHttp = new MockHttpMessageHandler(); mockHttp .Expect(HttpMethod.Delete, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.OK, new StringContent(APIResponses.DeleteTransaction)); + .Respond(HttpStatusCode.OK, new StringContent(JsonResources.DeleteTransaction)); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); await signhostApiClient.DeleteTransactionAsync("transaction Id"); } @@ -357,10 +391,10 @@ public async Task when_a_DeleteTransaction_with_notification_is_called_then_we_s .Expect(HttpMethod.Delete, "http://localhost/api/transaction/transaction Id") .WithHeaders("Content-Type", "application/json") //.With(matcher => matcher.Content.ToString().Contains("'SendNotifications': true")) - .Respond(HttpStatusCode.OK, new StringContent(APIResponses.DeleteTransaction)); + .Respond(HttpStatusCode.OK, new StringContent(JsonResources.DeleteTransaction)); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); await signhostApiClient.DeleteTransactionAsync( "transaction Id", @@ -381,7 +415,7 @@ public async Task when_AddOrReplaceFileToTransaction_is_called_then_we_should_ha .Respond(HttpStatusCode.OK); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); // Create a 0 sized file using (Stream file = System.IO.File.Create("unittestdocument.pdf")) { @@ -402,7 +436,7 @@ public async Task when_AddOrReplaceFileToTransaction_is_called_default_digest_is .Respond(HttpStatusCode.OK); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); await signhostApiClient.AddOrReplaceFileToTransaction(new MemoryStream(), "transaction Id", "file Id"); } @@ -420,7 +454,7 @@ public async Task when_AddOrReplaceFileToTransaction_with_sha512_is_called_defau .Respond(HttpStatusCode.OK); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); await signhostApiClient.AddOrReplaceFileToTransactionAsync( new MemoryStream(), @@ -447,7 +481,7 @@ public async Task when_AddOrReplaceFileToTransaction_with_digest_value_is_used_a .Respond(HttpStatusCode.OK); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); await signhostApiClient.AddOrReplaceFileToTransactionAsync( new MemoryStream(), @@ -475,7 +509,7 @@ public async Task when_StartTransaction_is_called_then_we_should_have_called_the using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); await signhostApiClient.StartTransactionAsync("transaction Id"); } @@ -491,7 +525,7 @@ public async Task when_GetReceipt_is_called_then_we_should_have_called_the_filer .Respond(HttpStatusCode.OK); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); var receipt = await signhostApiClient.GetReceiptAsync("transaction ID"); } @@ -508,7 +542,7 @@ public async Task when_GetDocument_is_called_then_we_should_have_called_the_file using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); var document = await signhostApiClient.GetDocumentAsync("transaction Id", "file Id"); } @@ -521,11 +555,11 @@ public async Task When_a_transaction_json_is_returned_it_is_deserialized_correct { var mockHttp = new MockHttpMessageHandler(); mockHttp.Expect(HttpMethod.Post, "http://localhost/api/transaction") - .Respond(HttpStatusCode.OK, new StringContent(RequestBodies.TransactionSingleSignerJson)); + .Respond(HttpStatusCode.OK, new StringContent(JsonResources.TransactionSingleSignerJson)); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); var result = await signhostApiClient.CreateTransactionAsync(new Transaction { @@ -544,7 +578,7 @@ public async Task When_a_transaction_json_is_returned_it_is_deserialized_correct }); result.Id.Should().Be("50262c3f-9744-45bf-a4c6-8a3whatever"); - result.CancelledDateTime.Should().HaveYear(2017); + result.CanceledDateTime.Should().HaveYear(2017); result.Status.Should().Be(TransactionStatus.WaitingForDocument); result.Signers.Should().HaveCount(1); result.Receivers.Should().HaveCount(0); @@ -589,9 +623,9 @@ MockedRequest AddHeaders(MockedRequest request) var mockHttp = new MockHttpMessageHandler(); AddHeaders(mockHttp.Expect(HttpMethod.Post, "http://localhost/api/transaction")) - .Respond(new StringContent(RequestBodies.TransactionSingleSignerJson)); + .Respond(new StringContent(JsonResources.TransactionSingleSignerJson)); AddHeaders(mockHttp.Expect(HttpMethod.Put, "http://localhost/api/transaction/*/file/somefileid")) - .Respond(HttpStatusCode.Accepted, new StringContent(RequestBodies.AddOrReplaceFileMetaToTransaction)); + .Respond(HttpStatusCode.Accepted, new StringContent(JsonResources.AddOrReplaceFileMetaToTransaction)); AddHeaders(mockHttp.Expect(HttpMethod.Put, "http://localhost/api/transaction/*/file/somefileid")) .Respond(HttpStatusCode.Created); AddHeaders(mockHttp.Expect(HttpMethod.Put, "http://localhost/api/transaction/*/start")) @@ -600,7 +634,7 @@ MockedRequest AddHeaders(MockedRequest request) using (var httpClient = mockHttp.ToHttpClient()) { var clientSettings = isOauth ? oauthSettings : settings; clientSettings.AddHeader = add => add("X-Custom", "test"); - var signhostApiClient = new SignHostApiClient(clientSettings, httpClient); + var signhostApiClient = new SignhostApiClient(clientSettings, httpClient); var result = await signhostApiClient.CreateTransactionAsync(new Transaction()); await signhostApiClient.AddOrReplaceFileMetaToTransactionAsync(new FileMeta(), result.Id, "somefileid"); @@ -614,43 +648,17 @@ MockedRequest AddHeaders(MockedRequest request) mockHttp.VerifyNoOutstandingRequest(); } - [Fact] - public async Task When_a_custom_verificationtype_is_provided_it_is_deserialized_correctly() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/c487be92-0255-40c7-bd7d-20805a65e7d9") - .Respond(new StringContent(APIResponses.GetTransactionCustomVerificationType)); - - SignHostApiClient.RegisterVerification(); - - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); - - var result = await signhostApiClient.GetTransactionAsync("c487be92-0255-40c7-bd7d-20805a65e7d9"); - - result.Signers[0].Verifications.Should().HaveCount(3); - result.Signers[0].Verifications[0].Should().BeOfType(); - result.Signers[0].Verifications[1].Should().BeOfType() - .Which.IPAddress.Should().Be("127.0.0.33"); - result.Signers[0].Verifications[2].Should().BeOfType() - .Which.Number.Should().Be("123"); - } - } - [Fact] public async Task When_a_minimal_response_is_retrieved_list_and_dictionaries_are_not_null() { var mockHttp = new MockHttpMessageHandler(); mockHttp .Expect(HttpMethod.Get, "http://localhost/api/transaction/c487be92-0255-40c7-bd7d-20805a65e7d9") - .Respond(new StringContent(APIResponses.MinimalTransactionResponse)); - - SignHostApiClient.RegisterVerification(); + .Respond(new StringContent(JsonResources.MinimalTransactionResponse)); using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + var signhostApiClient = new SignhostApiClient(settings, httpClient); var result = await signhostApiClient.GetTransactionAsync("c487be92-0255-40c7-bd7d-20805a65e7d9"); @@ -659,11 +667,5 @@ public async Task When_a_minimal_response_is_retrieved_list_and_dictionaries_are result.Files.Should().BeEmpty(); } } - - public class CustomVerification - : IVerification - { - public string Type => "CustomVerificationType"; - } } } diff --git a/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs b/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs index 79099cc4..ba6dd32a 100644 --- a/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs +++ b/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using RichardSzalay.MockHttp; using System.Net; +using SignhostAPIClient.Tests.JSON; namespace Signhost.APIClient.Rest.Tests { @@ -20,7 +21,7 @@ public void when_IsPostbackChecksumValid_is_called_with_valid_postback_in_body_t { // Arrange IDictionary headers = new Dictionary { { "Content-Type", new[] { "application/json" } } }; - string body = RequestBodies.MockPostbackValid; + string body = JsonResources.MockPostbackValid; // Act SignhostApiReceiver signhostApiReceiver = new SignhostApiReceiver(receiverSettings); @@ -35,7 +36,7 @@ public void when_IsPostbackChecksumValid_is_called_with_invalid_postback_in_body { // Arrange IDictionary headers = new Dictionary { { "Content-Type", new[] { "application/json" } } }; - string body = RequestBodies.MockPostbackInvalid; + string body = JsonResources.MockPostbackInvalid; // Act SignhostApiReceiver signhostApiReceiver = new SignhostApiReceiver(receiverSettings); @@ -53,7 +54,7 @@ public void when_IsPostbackChecksumValid_is_called_with_valid_postback_in_header { "Content-Type", new[] { "application/json" }}, {"Checksum", new[] {"cdc09eee2ed6df2846dcc193aedfef59f2834f8d"}} }; - string body = RequestBodies.MockPostbackValid; + string body = JsonResources.MockPostbackValid; // Act SignhostApiReceiver signhostApiReceiver = new SignhostApiReceiver(receiverSettings); @@ -71,7 +72,7 @@ public void when_IsPostbackChecksumValid_is_called_with_invalid_postback_in_head { "Content-Type", new[] { "application/json" }}, {"Checksum", new[] {"70dda90616f744797972c0d2f787f86643a60c83"}} }; - string body = RequestBodies.MockPostbackValid; + string body = JsonResources.MockPostbackValid; // Act SignhostApiReceiver signhostApiReceiver = new SignhostApiReceiver(receiverSettings); diff --git a/src/SignhostAPIClient.Tests/app.config b/src/SignhostAPIClient.Tests/app.config deleted file mode 100644 index de5386a4..00000000 --- a/src/SignhostAPIClient.Tests/app.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/SignhostAPIClient/Rest/DataObjects/Activity.cs b/src/SignhostAPIClient/Rest/DataObjects/Activity.cs index 31a703c8..517d255b 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Activity.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Activity.cs @@ -1,5 +1,5 @@ using System; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Signhost.APIClient.Rest.DataObjects { @@ -9,7 +9,7 @@ public class Activity public ActivityType Code { get; set; } - [JsonProperty("Activity")] + [JsonPropertyName("Activity")] public string ActivityValue { get; set; } public string Info { get; set; } diff --git a/src/SignhostAPIClient/Rest/DataObjects/ConsentVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/ConsentVerification.cs index 18c1cfd0..ec51d087 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ConsentVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/ConsentVerification.cs @@ -10,9 +10,5 @@ namespace Signhost.APIClient.Rest.DataObjects public class ConsentVerification : IVerification { - /// - /// Gets the . - /// - public string Type { get; } = "Consent"; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/CscVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/CscVerification.cs index 109074e0..2ceb0de4 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/CscVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/CscVerification.cs @@ -8,11 +8,6 @@ namespace Signhost.APIClient.Rest.DataObjects public class CscVerification : IVerification { - /// - /// Gets the . - /// - public string Type => "CSC Qualified"; - /// /// Gets or sets the provider identifier. /// diff --git a/src/SignhostAPIClient/Rest/DataObjects/DigidVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/DigidVerification.cs index c5be757f..bfb8b9f2 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/DigidVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/DigidVerification.cs @@ -3,8 +3,6 @@ public class DigidVerification : IVerification { - public string Type => "DigiD"; - public string Bsn { get; set; } public bool? SecureDownload { get; set; } diff --git a/src/SignhostAPIClient/Rest/DataObjects/EherkenningVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/EherkenningVerification.cs index adee19d9..647b1c0e 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/EherkenningVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/EherkenningVerification.cs @@ -3,11 +3,6 @@ namespace Signhost.APIClient.Rest.DataObjects public class EherkenningVerification : IVerification { - /// - /// Gets the . - /// - public string Type => "eHerkenning"; - /// /// Gets or sets the Uid. /// diff --git a/src/SignhostAPIClient/Rest/DataObjects/EidasLoginVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/EidasLoginVerification.cs index 72deeac5..18ac4afa 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/EidasLoginVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/EidasLoginVerification.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Text.Json.Serialization; +using Signhost.APIClient.Rest.JsonConverters; namespace Signhost.APIClient.Rest.DataObjects { @@ -9,11 +11,6 @@ namespace Signhost.APIClient.Rest.DataObjects public class EidasLoginVerification : IVerification { - /// - /// Gets the . - /// - public string Type { get; } = "eIDAS Login"; - /// /// Gets or sets the uid. /// @@ -22,6 +19,7 @@ public class EidasLoginVerification /// /// Gets or sets the level. /// + [JsonConverter(typeof(LevelEnumConverter))] public Level? Level { get; set; } /// diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileMeta.cs b/src/SignhostAPIClient/Rest/DataObjects/FileMeta.cs index 4c0f2eba..6cbda5a0 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileMeta.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileMeta.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Signhost.APIClient.Rest.DataObjects { @@ -21,7 +21,7 @@ public class FileMeta /// Don't use this setting unless you are really sure this is what you /// want and know the side-effects. /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public bool? SetParaph { get; set; } } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs index ea2f0eb8..5e789549 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Text; -using Newtonsoft.Json; namespace Signhost.APIClient.Rest.DataObjects { @@ -11,20 +10,6 @@ namespace Signhost.APIClient.Rest.DataObjects public class IPAddressVerification : IVerification { - /// - /// Initializes a new instance of the class. - /// Do not use! - /// - [Obsolete("This constructor is for internal usage only!")] - public IPAddressVerification() - { - } - - /// - /// Gets the . - /// - public string Type { get; } = "IPAddress"; - /// /// Gets or sets the IP Address used by the signer while signing the documents. /// diff --git a/src/SignhostAPIClient/Rest/DataObjects/IVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/IVerification.cs index 5ab6b93e..bbab8eac 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/IVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/IVerification.cs @@ -1,12 +1,24 @@ -using Newtonsoft.Json; -using Signhost.APIClient.Rest.JsonConverters; +using System.Text.Json.Serialization; namespace Signhost.APIClient.Rest.DataObjects { // TO-DO: Split to verification and authentication in v5 - [JsonConverter(typeof(JsonVerificationConverter))] + [JsonPolymorphic(TypeDiscriminatorPropertyName = "Type")] + [JsonDerivedType(typeof(ConsentVerification), "Consent")] + [JsonDerivedType(typeof(DigidVerification), "DigiD")] + [JsonDerivedType(typeof(EidasLoginVerification), "eIDAS Login")] + [JsonDerivedType(typeof(IdealVerification), "iDeal")] + [JsonDerivedType(typeof(IdinVerification), "iDIN")] + [JsonDerivedType(typeof(IPAddressVerification), "IPAddress")] + [JsonDerivedType(typeof(ItsmeIdentificationVerification), "itsme Identification")] + [JsonDerivedType(typeof(PhoneNumberVerification), "PhoneNumber")] + [JsonDerivedType(typeof(ScribbleVerification), "Scribble")] + [JsonDerivedType(typeof(SurfnetVerification), "SURFnet")] + [JsonDerivedType(typeof(CscVerification), "CSC Qualified")] + [JsonDerivedType(typeof(EherkenningVerification), "eHerkenning")] + [JsonDerivedType(typeof(OidcVerification), "OpenID Providers")] + [JsonDerivedType(typeof(OnfidoVerification), "Onfido")] public interface IVerification { - string Type { get; } } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/IdealVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/IdealVerification.cs index 459e159b..5267f284 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/IdealVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/IdealVerification.cs @@ -3,8 +3,6 @@ public class IdealVerification : IVerification { - public string Type => "iDeal"; - public string Iban { get; set; } public string AccountHolderName { get; set; } diff --git a/src/SignhostAPIClient/Rest/DataObjects/IdinVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/IdinVerification.cs index 684438c6..38adcd92 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/IdinVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/IdinVerification.cs @@ -6,8 +6,6 @@ namespace Signhost.APIClient.Rest.DataObjects public class IdinVerification : IVerification { - public string Type { get; } = "iDIN"; - public string AccountHolderName { get; set; } public string AccountHolderAddress1 { get; set; } diff --git a/src/SignhostAPIClient/Rest/DataObjects/ItsmeIdentificationVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/ItsmeIdentificationVerification.cs index c2c069e6..eeae90b8 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ItsmeIdentificationVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/ItsmeIdentificationVerification.cs @@ -6,11 +6,6 @@ namespace Signhost.APIClient.Rest.DataObjects public class ItsmeIdentificationVerification : IVerification { - /// - /// Gets the . - /// - public string Type => "itsme Identification"; - /// /// Gets or sets the phonenumber. /// diff --git a/src/SignhostAPIClient/Rest/DataObjects/ItsmeSignVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/ItsmeSignVerification.cs deleted file mode 100644 index 20699888..00000000 --- a/src/SignhostAPIClient/Rest/DataObjects/ItsmeSignVerification.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Signhost.APIClient.Rest.DataObjects -{ - /// - /// Verification object for itsme sign. - /// - public class ItsmeSignVerification - : IVerification - { - /// - /// Gets the . - /// - public string Type => "itsme sign"; - } -} diff --git a/src/SignhostAPIClient/Rest/DataObjects/KennisnetVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/KennisnetVerification.cs deleted file mode 100644 index 72546591..00000000 --- a/src/SignhostAPIClient/Rest/DataObjects/KennisnetVerification.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Signhost.APIClient.Rest.DataObjects -{ - [Obsolete("This verification is no longer supported and will be removed in SemVer 4.")] - public class KennisnetVerification - : IVerification - { - public string Type => "Kennisnet"; - } -} diff --git a/src/SignhostAPIClient/Rest/DataObjects/Level.cs b/src/SignhostAPIClient/Rest/DataObjects/Level.cs index 40b016c1..93f223b1 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Level.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Level.cs @@ -1,12 +1,8 @@ -using Newtonsoft.Json; -using Signhost.APIClient.Rest.JsonConverters; - namespace Signhost.APIClient.Rest.DataObjects { /// /// Level of Assurance. /// - [JsonConverter(typeof(LevelEnumConverter))] public enum Level { /// diff --git a/src/SignhostAPIClient/Rest/DataObjects/OidcVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/OidcVerification.cs index 208b012d..57507de1 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/OidcVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/OidcVerification.cs @@ -6,11 +6,6 @@ namespace Signhost.APIClient.Rest.DataObjects public class OidcVerification : IVerification { - /// - /// Gets the . - /// - public string Type => "OpenID Providers"; - /// /// Gets or sets the OIDC provider name. /// diff --git a/src/SignhostAPIClient/Rest/DataObjects/OnfidoVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/OnfidoVerification.cs index 7a9d166f..ae2a6a3e 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/OnfidoVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/OnfidoVerification.cs @@ -9,11 +9,6 @@ namespace Signhost.APIClient.Rest.DataObjects public class OnfidoVerification : IVerification { - /// - /// Gets the . - /// - public string Type => "Onfido"; - /// /// Gets or sets the Onfido workflow identifier. /// diff --git a/src/SignhostAPIClient/Rest/DataObjects/PhoneNumberVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/PhoneNumberVerification.cs index 14b3870f..6881543d 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/PhoneNumberVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/PhoneNumberVerification.cs @@ -3,8 +3,6 @@ public class PhoneNumberVerification : IVerification { - public string Type => "PhoneNumber"; - public string Number { get; set; } public bool? SecureDownload { get; set; } diff --git a/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs b/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs index 4c5db856..76a2726e 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs @@ -1,27 +1,13 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Signhost.APIClient.Rest.DataObjects { public class PostbackTransaction : Transaction { - public PostbackTransaction() - { - } - - [JsonConstructor] - private PostbackTransaction( - IReadOnlyDictionary files, - DateTimeOffset? createdDateTime, - DateTimeOffset? canceledDateTime, - string cancelationReason) - : base(files, createdDateTime, canceledDateTime, cancelationReason) - { - } - - [JsonProperty("Checksum")] + [JsonPropertyName("Checksum")] public string Checksum { get; set; } } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Receiver.cs b/src/SignhostAPIClient/Rest/DataObjects/Receiver.cs index 5f446c72..be820504 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Receiver.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Receiver.cs @@ -1,6 +1,6 @@ -using System; +using System; using System.Collections.Generic; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Signhost.APIClient.Rest.DataObjects { diff --git a/src/SignhostAPIClient/Rest/DataObjects/ScribbleVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/ScribbleVerification.cs index 64f18a75..fadb57af 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ScribbleVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/ScribbleVerification.cs @@ -3,8 +3,6 @@ public class ScribbleVerification : IVerification { - public string Type => "Scribble"; - public bool RequireHandsignature { get; set; } public bool ScribbleNameFixed { get; set; } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Signer.cs b/src/SignhostAPIClient/Rest/DataObjects/Signer.cs index e3c88700..5d61f23e 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Signer.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Signer.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Signhost.APIClient.Rest.DataObjects { diff --git a/src/SignhostAPIClient/Rest/DataObjects/SigningCertificateVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/SigningCertificateVerification.cs deleted file mode 100644 index 98673fd3..00000000 --- a/src/SignhostAPIClient/Rest/DataObjects/SigningCertificateVerification.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Signhost.APIClient.Rest.DataObjects -{ - /// - /// Represents a verification method using a signers signing certificate - /// for example a qualified certificate. - /// - public class SigningCertificateVerification - : IVerification - { - /// - public string Type => "SigningCertificate"; - } -} diff --git a/src/SignhostAPIClient/Rest/DataObjects/SurfnetVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/SurfnetVerification.cs index c65632fb..dfef6fdb 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/SurfnetVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/SurfnetVerification.cs @@ -5,8 +5,6 @@ namespace Signhost.APIClient.Rest.DataObjects public class SurfnetVerification : IVerification { - public string Type => "SURFnet"; - public string Uid { get; set; } public IDictionary Attributes { get; set; } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs b/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs index 534d31e6..35dbb133 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs @@ -1,55 +1,41 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Signhost.APIClient.Rest.DataObjects { public class Transaction { - public Transaction() - { - } - - [JsonConstructor] - protected Transaction( - IReadOnlyDictionary files, - DateTimeOffset? createdDateTime, - DateTimeOffset? canceledDateTime, - string cancelationReason) - { - Files = files ?? new Dictionary(); - CreatedDateTime = createdDateTime; - CancelledDateTime = canceledDateTime; - CancellationReason = cancelationReason; - } - public string Id { get; set; } /// /// Gets the when the was created. /// - public DateTimeOffset? CreatedDateTime { get; } + public DateTimeOffset? CreatedDateTime { get; set; } /// /// Gets the when the was cancelled. /// Returns null if the transaction was not cancelled. /// - public DateTimeOffset? CancelledDateTime { get; } + public DateTimeOffset? CanceledDateTime { get; set; } /// /// Gets the cancellation reason when the was cancelled. /// - public string CancellationReason { get; } + public string CancellationReason { get; set; } - public IReadOnlyDictionary Files { get; private set; } + public IReadOnlyDictionary Files { get; set; } = + new Dictionary(); public TransactionStatus Status { get; set; } public bool Seal { get; set; } - public IList Signers { get; set; } = new List(); + public IList Signers { get; set; } + = new List(); - public IList Receivers { get; set; } = new List(); + public IList Receivers { get; set; } + = new List(); public string Reference { get; set; } diff --git a/src/SignhostAPIClient/Rest/DataObjects/UnknownVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/UnknownVerification.cs deleted file mode 100644 index 69c17ba3..00000000 --- a/src/SignhostAPIClient/Rest/DataObjects/UnknownVerification.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Signhost.APIClient.Rest.DataObjects -{ - public class UnknownVerification - : IVerification - { - public string Type { get; set; } - } -} diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs b/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs index 3ad5df5a..55e09ec3 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs @@ -2,8 +2,9 @@ using System.Linq; using System.Net; using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; -using Newtonsoft.Json; namespace Signhost.APIClient.Rest.ErrorHandling { @@ -64,15 +65,10 @@ public static async Task EnsureSignhostSuccessStatusCodeAsy responseBody = await response.Content.ReadAsStringAsync() .ConfigureAwait(false); - var error = JsonConvert.DeserializeAnonymousType( - responseBody, - new { - Type = string.Empty, - Message = string.Empty, - }); + var error = JsonSerializer.Deserialize(responseBody); - errorType = error.Type; - errorMessage = error.Message; + errorType = error?.Type ?? string.Empty; + errorMessage = error?.Message ?? "Unknown Signhost error"; } // TO-DO: Use switch pattern in v5 @@ -116,5 +112,14 @@ public static async Task EnsureSignhostSuccessStatusCodeAsy throw exception; } + + private class ErrorResponse + { + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("message")] + public string Message { get; set; } + } } } diff --git a/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs b/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs index e205e41a..b7e2836e 100644 --- a/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs +++ b/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs @@ -1,6 +1,6 @@ using System.Net.Http; +using System.Text.Json; using System.Threading.Tasks; -using Newtonsoft.Json; namespace Signhost.APIClient.Rest { @@ -25,7 +25,7 @@ internal static async Task FromJsonAsync( var json = await httpContent.ReadAsStringAsync() .ConfigureAwait(false); - return JsonConvert.DeserializeObject(json); + return JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); } } } diff --git a/src/SignhostAPIClient/Rest/ISignHostApiClient.cs b/src/SignhostAPIClient/Rest/ISignHostApiClient.cs index f4546fd8..79c5a885 100644 --- a/src/SignhostAPIClient/Rest/ISignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/ISignHostApiClient.cs @@ -8,7 +8,7 @@ namespace Signhost.APIClient.Rest /// /// Interface abstracting the available Signhost API calls. /// - public interface ISignHostApiClient + public interface ISignhostApiClient { /// /// Creates a new transaction. diff --git a/src/SignhostAPIClient/Rest/ISignhostApiClientSettings.cs b/src/SignhostAPIClient/Rest/ISignhostApiClientSettings.cs index 439dc127..34c7c920 100644 --- a/src/SignhostAPIClient/Rest/ISignhostApiClientSettings.cs +++ b/src/SignhostAPIClient/Rest/ISignhostApiClientSettings.cs @@ -4,7 +4,7 @@ namespace Signhost.APIClient.Rest { public delegate void AddHeaders(string name, string value); - public interface ISignHostApiClientSettings + public interface ISignhostApiClientSettings { /// /// Gets the usertoken identifying an authorized user. diff --git a/src/SignhostAPIClient/Rest/JsonContent.cs b/src/SignhostAPIClient/Rest/JsonContent.cs index 54e72b4c..295054aa 100644 --- a/src/SignhostAPIClient/Rest/JsonContent.cs +++ b/src/SignhostAPIClient/Rest/JsonContent.cs @@ -1,6 +1,6 @@ using System.Net.Http; using System.Net.Http.Headers; -using Newtonsoft.Json; +using System.Text.Json; namespace Signhost.APIClient.Rest { @@ -40,7 +40,7 @@ public JsonContent(T value) private static string ToJson(T value) { - return JsonConvert.SerializeObject(value); + return JsonSerializer.Serialize(value, SignhostJsonSerializerOptions.Default); } } } diff --git a/src/SignhostAPIClient/Rest/JsonConverters/JsonBaseConverter.cs b/src/SignhostAPIClient/Rest/JsonConverters/JsonBaseConverter.cs deleted file mode 100644 index ff5642bd..00000000 --- a/src/SignhostAPIClient/Rest/JsonConverters/JsonBaseConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Reflection; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace Signhost.APIClient.Rest.JsonConverters -{ - public abstract class JsonBaseConverter - : JsonConverter - { - public override bool CanConvert(Type objectType) - => typeof(T) -#if TYPEINFO - .GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo()); -#else - .IsAssignableFrom(objectType); -#endif - - public override object ReadJson( - JsonReader reader, - Type objectType, - object existingValue, - JsonSerializer serializer) - { - var jsonObject = JObject.Load(reader); - var target = Create(objectType, jsonObject); - serializer.Populate(jsonObject.CreateReader(), target); - return target; - } - - protected abstract T Create(Type objectType, JObject jsonObject); - } -} diff --git a/src/SignhostAPIClient/Rest/JsonConverters/JsonVerificationConverter.cs b/src/SignhostAPIClient/Rest/JsonConverters/JsonVerificationConverter.cs deleted file mode 100644 index 4e1f9fa6..00000000 --- a/src/SignhostAPIClient/Rest/JsonConverters/JsonVerificationConverter.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Signhost.APIClient.Rest.DataObjects; - -namespace Signhost.APIClient.Rest.JsonConverters -{ - internal class JsonVerificationConverter - : JsonBaseConverter - { -#if TYPEINFO - private static readonly IDictionary VerificationTypes = -#else - private static readonly IDictionary VerificationTypes = -#endif - CreateVerificationTypeMap(); - - public override bool CanWrite - => false; - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - => new NotImplementedException(); - - /// - /// Adds an additional verification type to the - /// map. - /// - /// - internal static void RegisterVerification() - where T : IVerification - { - var verification = (IVerification)Activator.CreateInstance(typeof(T)); - - VerificationTypes[verification.Type] = -#if TYPEINFO - typeof(T).GetTypeInfo(); -#else - typeof(T); -#endif - } - - protected override IVerification Create( - Type objectType, - JObject jsonObject) - { - var typeName = jsonObject["Type"]?.ToString(); - - if (VerificationTypes.TryGetValue(typeName, out var verificationType)) { -#if TYPEINFO - return (IVerification)Activator.CreateInstance(verificationType.AsType()); -#else - return (IVerification)Activator.CreateInstance(verificationType); -#endif - } - - return new UnknownVerification(); - } - -#if TYPEINFO - private static Dictionary CreateVerificationTypeMap() - { - return typeof(JsonVerificationConverter).GetTypeInfo().Assembly.ExportedTypes - .Select(t => t.GetTypeInfo()) - .Where(t => typeof(IVerification).GetTypeInfo().IsAssignableFrom(t)) - .Where(t => !t.IsInterface && !t.IsAbstract) -#pragma warning disable SA1008 // Opening parenthesis must be spaced correctly - .Select(t => ( - typeInfo: t, - instance: (IVerification)Activator.CreateInstance(t.AsType()))) -#pragma warning restore SA1008 // Opening parenthesis must be spaced correctly - .Where(t => t.instance.Type != null) - .ToDictionary(t => t.instance.Type, t => t.typeInfo); - } -#else - private static Dictionary CreateVerificationTypeMap() - { - return typeof(JsonVerificationConverter).Assembly.ExportedTypes - .Where(t => typeof(IVerification).IsAssignableFrom(t)) - .Where(t => !t.IsInterface && !t.IsAbstract) -#pragma warning disable SA1008 // Opening parenthesis must be spaced correctly - .Select(t => ( - type: t, - instance: (IVerification)Activator.CreateInstance(t))) -#pragma warning restore SA1008 // Opening parenthesis must be spaced correctly - .Where(t => t.instance.Type is not null) - .ToDictionary(t => t.instance.Type, t => t.type); - } -#endif - } -} diff --git a/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs b/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs index e0e5aeed..07c20313 100644 --- a/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs +++ b/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs @@ -1,69 +1,56 @@ using System; -using System.Reflection; -using Newtonsoft.Json; +using System.Text.Json; +using System.Text.Json.Serialization; using Signhost.APIClient.Rest.DataObjects; namespace Signhost.APIClient.Rest.JsonConverters { /// - /// JSON converter for converting the enum. + /// JSON converter factory for converting the enum. /// Invalid values are mapped to . /// internal class LevelEnumConverter - : JsonConverter + : JsonConverter { - /// - public override bool CanWrite => false; - - /// - public override bool CanConvert(Type objectType) - => IsLevelEnum(GetUnderlyingType(objectType)); - - /// - public override object ReadJson( - JsonReader reader, - Type objectType, - object existingValue, - JsonSerializer serializer) + public override Level? Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) { - var value = reader.Value as string; + if (reader.TokenType == JsonTokenType.Null) { + return null; + } - if (value != null) { - if (Enum.TryParse(value, out Level level)) { + if (reader.TokenType == JsonTokenType.String) { + var value = reader.GetString() ?? string.Empty; + if (Enum.TryParse(value, out var level)) { return level; } return Level.Unknown; } - return null; - } - - /// - public override void WriteJson( - JsonWriter writer, - object value, - JsonSerializer serializer) - => throw new NotImplementedException(); + if (reader.TokenType == JsonTokenType.Number) { + int value = reader.GetInt32(); + if (Enum.IsDefined(typeof(Level), value)) { + return (Level)value; + } + } - private static Type GetUnderlyingType(Type type) - => -#if TYPEINFO - type.GetTypeInfo().IsGenericType && -#else - type.IsGenericType && -#endif - type.GetGenericTypeDefinition() == typeof(Nullable<>) - ? Nullable.GetUnderlyingType(type) - : type; + return Level.Unknown; + } - private static bool IsLevelEnum(Type type) - => -#if TYPEINFO - type.GetTypeInfo().IsEnum && -#else - type.IsEnum && -#endif - type == typeof(Level); + public override void Write( + Utf8JsonWriter writer, + Level? value, + JsonSerializerOptions options) + { + if (value is null) { + writer.WriteNullValue(); + } + else { + writer.WriteStringValue(value.ToString()); + } + } } } diff --git a/src/SignhostAPIClient/Rest/SignHostApiClient.cs b/src/SignhostAPIClient/Rest/SignHostApiClient.cs index dd2e3a89..780aa1ba 100644 --- a/src/SignhostAPIClient/Rest/SignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/SignHostApiClient.cs @@ -12,43 +12,43 @@ namespace Signhost.APIClient.Rest { /// - /// Implements the interface which provides + /// Implements the interface which provides /// an signhost api client implementation. /// - public class SignHostApiClient - : ISignHostApiClient + public class SignhostApiClient + : ISignhostApiClient , IDisposable { private const string ApiVersion = "v1"; - private static readonly string Version = typeof(SignHostApiClient) + private static readonly string Version = typeof(SignhostApiClient) #if TYPEINFO .GetTypeInfo() #endif .Assembly.GetCustomAttribute() .Version; - private readonly ISignHostApiClientSettings settings; + private readonly ISignhostApiClientSettings settings; private readonly HttpClient client; /// - /// Initializes a new instance of the class. - /// Set your usertoken and APPKey by creating a . + /// Initializes a new instance of the class. + /// Set your usertoken and APPKey by creating a . /// - /// - public SignHostApiClient(ISignHostApiClientSettings settings) + /// + public SignhostApiClient(ISignhostApiClientSettings settings) : this(settings, new HttpClient()) { } /// - /// Initializes a new instance of the class. - /// Set your usertoken and APPKey by creating a . + /// Initializes a new instance of the class. + /// Set your usertoken and APPKey by creating a . /// - /// + /// /// to use for all http calls. - public SignHostApiClient( - ISignHostApiClientSettings settings, + public SignhostApiClient( + ISignhostApiClientSettings settings, HttpClient httpClient) { this.settings = settings; @@ -75,16 +75,6 @@ private string ApplicationHeader private string AuthorizationHeader => $"APIKey {settings.UserToken}"; - /// - /// Globally register an additional verification type. - /// - /// to - public static void RegisterVerification() - where T : IVerification - { - JsonConverters.JsonVerificationConverter.RegisterVerification(); - } - /// public async Task CreateTransactionAsync( Transaction transaction) diff --git a/src/SignhostAPIClient/Rest/SignHostApiClientSettings.cs b/src/SignhostAPIClient/Rest/SignHostApiClientSettings.cs index f93a6c9b..b4afed65 100644 --- a/src/SignhostAPIClient/Rest/SignHostApiClientSettings.cs +++ b/src/SignhostAPIClient/Rest/SignHostApiClientSettings.cs @@ -2,18 +2,18 @@ namespace Signhost.APIClient.Rest { - public class SignHostApiClientSettings - : ISignHostApiClientSettings + public class SignhostApiClientSettings + : ISignhostApiClientSettings { public const string DefaultEndpoint = "https://api.signhost.com/api/"; - public SignHostApiClientSettings(string appkey, string userToken) + public SignhostApiClientSettings(string appkey, string userToken) { APPKey = appkey; UserToken = userToken; } - public SignHostApiClientSettings(string appkey) + public SignhostApiClientSettings(string appkey) { APPKey = appkey; } diff --git a/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs b/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs index a3bfde6a..bdffe930 100644 --- a/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs +++ b/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Security.Cryptography; using System.Text; -using Newtonsoft.Json; +using System.Text.Json; using Signhost.APIClient.Rest; using Signhost.APIClient.Rest.DataObjects; @@ -64,12 +64,10 @@ private string CalculateChecksumFromPostback(PostbackTransaction postback) } } - private PostbackTransaction DeserializeToPostbackTransaction(string body) - { - return JsonConvert.DeserializeObject(body); - } - - private string GetChecksumFromHeadersOrPostback( + private PostbackTransaction DeserializeToPostbackTransaction(string body) + { + return JsonSerializer.Deserialize(body, SignhostJsonSerializerOptions.Default); + } private string GetChecksumFromHeadersOrPostback( IDictionary headers, PostbackTransaction postback) { diff --git a/src/SignhostAPIClient/Rest/SignhostJsonSerializerOptions.cs b/src/SignhostAPIClient/Rest/SignhostJsonSerializerOptions.cs new file mode 100644 index 00000000..37845021 --- /dev/null +++ b/src/SignhostAPIClient/Rest/SignhostJsonSerializerOptions.cs @@ -0,0 +1,23 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Signhost.APIClient.Rest +{ + /// + /// Centralized JSON serialization options for Signhost API. + /// + public static class SignhostJsonSerializerOptions + { + /// + /// Gets the default JSON serializer options. + /// + public static JsonSerializerOptions Default { get; } = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { + new JsonStringEnumConverter(), + }, + }; + } +} diff --git a/src/SignhostAPIClient/SignhostAPIClient.csproj b/src/SignhostAPIClient/SignhostAPIClient.csproj index 8c6a83a0..7309d334 100644 --- a/src/SignhostAPIClient/SignhostAPIClient.csproj +++ b/src/SignhostAPIClient/SignhostAPIClient.csproj @@ -42,7 +42,7 @@ - + From 62103a6a8ed02bfd01ea30a496466abe826cdb49 Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Fri, 21 Nov 2025 18:55:04 +0100 Subject: [PATCH 03/22] Remove NCrunch --- .gitattributes | 3 -- .gitignore | 5 ---- .../SignhostAPIClient.Tests.v2.ncrunchproject | 26 ----------------- src/SignhostAPIClient.v2.ncrunchsolution | 14 --------- src/SignhostAPIClient.v3.ncrunchsolution | 6 ---- .../SignhostAPIClient.v2.ncrunchproject | 29 ------------------- 6 files changed, 83 deletions(-) delete mode 100644 src/SignhostAPIClient.Tests/SignhostAPIClient.Tests.v2.ncrunchproject delete mode 100644 src/SignhostAPIClient.v2.ncrunchsolution delete mode 100644 src/SignhostAPIClient.v3.ncrunchsolution delete mode 100644 src/SignhostAPIClient/SignhostAPIClient.v2.ncrunchproject diff --git a/.gitattributes b/.gitattributes index 4eb59c0a..48d3a289 100644 --- a/.gitattributes +++ b/.gitattributes @@ -16,9 +16,6 @@ *.edmx text *.resx text -*.ncrunchproject text -*.ncrunchsolution text - # Custom for Visual Studio *.sln text eol=crlf merge=union *.csproj text diff --git a/.gitignore b/.gitignore index 7494d73c..0e70063f 100644 --- a/.gitignore +++ b/.gitignore @@ -104,11 +104,6 @@ _TeamCity* # DotCover is a Code Coverage Tool *.dotCover -# NCrunch -_NCrunch_* -.*crunch*.local.xml -*.v3.ncrunchsolution.user - # MightyMoose *.mm.* AutoTest.Net/ diff --git a/src/SignhostAPIClient.Tests/SignhostAPIClient.Tests.v2.ncrunchproject b/src/SignhostAPIClient.Tests/SignhostAPIClient.Tests.v2.ncrunchproject deleted file mode 100644 index 1f00ab7c..00000000 --- a/src/SignhostAPIClient.Tests/SignhostAPIClient.Tests.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/SignhostAPIClient.v2.ncrunchsolution b/src/SignhostAPIClient.v2.ncrunchsolution deleted file mode 100644 index b98737f1..00000000 --- a/src/SignhostAPIClient.v2.ncrunchsolution +++ /dev/null @@ -1,14 +0,0 @@ - - 1 - false - false - true - UseDynamicAnalysis - UseStaticAnalysis - UseStaticAnalysis - UseStaticAnalysis - UseDynamicAnalysis - - - - \ No newline at end of file diff --git a/src/SignhostAPIClient.v3.ncrunchsolution b/src/SignhostAPIClient.v3.ncrunchsolution deleted file mode 100644 index ff7af7e3..00000000 --- a/src/SignhostAPIClient.v3.ncrunchsolution +++ /dev/null @@ -1,6 +0,0 @@ - - - True - True - - diff --git a/src/SignhostAPIClient/SignhostAPIClient.v2.ncrunchproject b/src/SignhostAPIClient/SignhostAPIClient.v2.ncrunchproject deleted file mode 100644 index 3f48b966..00000000 --- a/src/SignhostAPIClient/SignhostAPIClient.v2.ncrunchproject +++ /dev/null @@ -1,29 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - - - - \ No newline at end of file From fe3875c80fb91fee86ba74e87a5068408fc1fb67 Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Fri, 21 Nov 2025 18:57:58 +0100 Subject: [PATCH 04/22] Remove unused using directives --- src/SignhostAPIClient.Tests/SignhostApiClientTests.cs | 1 - src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs | 8 +------- src/SignhostAPIClient/Rest/ApiResponse.cs | 5 +---- src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs | 6 +----- .../Rest/DataObjects/ConsentVerification.cs | 6 +----- .../Rest/DataObjects/IPAddressVerification.cs | 4 ---- .../Rest/DataObjects/PostbackTransaction.cs | 2 -- src/SignhostAPIClient/Rest/DataObjects/Transaction.cs | 1 - .../Rest/ErrorHandling/BadAuthorizationException.cs | 5 ----- .../Rest/ErrorHandling/BadRequestException.cs | 1 - .../Rest/ErrorHandling/DefaultSignhostException.cs | 1 - src/SignhostAPIClient/Rest/ErrorHandling/GoneException.cs | 1 - .../Rest/ErrorHandling/InternalServerErrorException.cs | 1 - .../Rest/ErrorHandling/NotFoundException.cs | 1 - .../Rest/ErrorHandling/SignhostException.cs | 1 - .../Rest/ErrorHandling/SignhostRestApiClientException.cs | 1 - src/SignhostAPIClient/Rest/FileDigestOptions.cs | 6 +----- src/SignhostAPIClient/Rest/ISignhostApiReceiver.cs | 3 --- src/SignhostAPIClient/Rest/SignhostApiReceiverSettings.cs | 4 +--- 19 files changed, 6 insertions(+), 52 deletions(-) diff --git a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs index 0e5f1e9b..fc3c085a 100644 --- a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs +++ b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs @@ -9,7 +9,6 @@ using RichardSzalay.MockHttp; using System.Net; using SignhostAPIClient.Tests.JSON; -using System.Text.Json.Serialization; namespace Signhost.APIClient.Rest.Tests { diff --git a/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs b/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs index ba6dd32a..198f95bf 100644 --- a/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs +++ b/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs @@ -1,13 +1,7 @@ -using System; -using System.Threading.Tasks; -using System.Net.Http; -using System.IO; -using Xunit; +using Xunit; using Signhost.APIClient.Rest.DataObjects; using FluentAssertions; using System.Collections.Generic; -using RichardSzalay.MockHttp; -using System.Net; using SignhostAPIClient.Tests.JSON; namespace Signhost.APIClient.Rest.Tests diff --git a/src/SignhostAPIClient/Rest/ApiResponse.cs b/src/SignhostAPIClient/Rest/ApiResponse.cs index 880dcd3c..42e21340 100644 --- a/src/SignhostAPIClient/Rest/ApiResponse.cs +++ b/src/SignhostAPIClient/Rest/ApiResponse.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Net; +using System.Net; using System.Net.Http; -using System.Text; namespace Signhost.APIClient.Rest { diff --git a/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs b/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs index 4b85586e..576f9eb1 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects { /// /// type. diff --git a/src/SignhostAPIClient/Rest/DataObjects/ConsentVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/ConsentVerification.cs index ec51d087..00289618 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ConsentVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/ConsentVerification.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects { /// /// Adds a consent verification screen diff --git a/src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs index 5e789549..cb68fcda 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Text; - namespace Signhost.APIClient.Rest.DataObjects { /// diff --git a/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs b/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs index 76a2726e..ea57648d 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs @@ -1,5 +1,3 @@ -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; namespace Signhost.APIClient.Rest.DataObjects diff --git a/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs b/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs index 35dbb133..66166334 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text.Json.Serialization; namespace Signhost.APIClient.Rest.DataObjects { diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs index 7dc3a198..63dde05c 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs @@ -1,9 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; namespace Signhost.APIClient.Rest.ErrorHandling { diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/BadRequestException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/BadRequestException.cs index 8fb0d528..d6041abf 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/BadRequestException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/BadRequestException.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.Serialization; namespace Signhost.APIClient.Rest.ErrorHandling { diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/DefaultSignhostException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/DefaultSignhostException.cs index ca6fb125..8c9a0542 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/DefaultSignhostException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/DefaultSignhostException.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.Serialization; namespace Signhost.APIClient.Rest.ErrorHandling { diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/GoneException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/GoneException.cs index b902fe62..08e8ce8c 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/GoneException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/GoneException.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.Serialization; namespace Signhost.APIClient.Rest.ErrorHandling { diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/InternalServerErrorException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/InternalServerErrorException.cs index 927c49f2..0100e317 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/InternalServerErrorException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/InternalServerErrorException.cs @@ -1,6 +1,5 @@ using System; using System.Net.Http.Headers; -using System.Runtime.Serialization; namespace Signhost.APIClient.Rest.ErrorHandling { diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/NotFoundException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/NotFoundException.cs index ff21de45..6c9eda47 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/NotFoundException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/NotFoundException.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.Serialization; namespace Signhost.APIClient.Rest.ErrorHandling { diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/SignhostException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/SignhostException.cs index a0e7bcfd..2e5db3d0 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/SignhostException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/SignhostException.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.Serialization; namespace Signhost.APIClient.Rest.ErrorHandling { diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs index ff620e0a..e437036d 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.Serialization; namespace Signhost.APIClient.Rest.ErrorHandling { diff --git a/src/SignhostAPIClient/Rest/FileDigestOptions.cs b/src/SignhostAPIClient/Rest/FileDigestOptions.cs index 02d42487..b71e0eb7 100644 --- a/src/SignhostAPIClient/Rest/FileDigestOptions.cs +++ b/src/SignhostAPIClient/Rest/FileDigestOptions.cs @@ -1,8 +1,4 @@ -using System; -using System.IO; -using System.Security.Cryptography; - -namespace Signhost.APIClient.Rest +namespace Signhost.APIClient.Rest { /// /// File digest options for file uploads diff --git a/src/SignhostAPIClient/Rest/ISignhostApiReceiver.cs b/src/SignhostAPIClient/Rest/ISignhostApiReceiver.cs index ac8441a7..7778debe 100644 --- a/src/SignhostAPIClient/Rest/ISignhostApiReceiver.cs +++ b/src/SignhostAPIClient/Rest/ISignhostApiReceiver.cs @@ -1,7 +1,4 @@ -using System; using System.Collections.Generic; -using System.Threading.Tasks; -using Signhost.APIClient.Rest; using Signhost.APIClient.Rest.DataObjects; namespace Signhost.APIClient.Rest diff --git a/src/SignhostAPIClient/Rest/SignhostApiReceiverSettings.cs b/src/SignhostAPIClient/Rest/SignhostApiReceiverSettings.cs index cb8fd629..d3d962aa 100644 --- a/src/SignhostAPIClient/Rest/SignhostApiReceiverSettings.cs +++ b/src/SignhostAPIClient/Rest/SignhostApiReceiverSettings.cs @@ -1,6 +1,4 @@ -using System; - -namespace Signhost.APIClient.Rest +namespace Signhost.APIClient.Rest { /// /// Registers the necessary settings for the class. From a39cec55fa15e763ac6169b477b63830aa83d39e Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Fri, 21 Nov 2025 19:06:48 +0100 Subject: [PATCH 05/22] Fix or suppress StyleCop Analyzer warnings --- src/SignhostAPIClient/Rest/JsonContent.cs | 10 +++++----- src/SignhostAPIClient/Rest/SignHostApiClient.cs | 5 +++-- src/SignhostAPIClient/Rest/SignhostApiReceiver.cs | 14 +++++++++----- src/signhost.ruleset | 4 ++++ 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/SignhostAPIClient/Rest/JsonContent.cs b/src/SignhostAPIClient/Rest/JsonContent.cs index 295054aa..837d627e 100644 --- a/src/SignhostAPIClient/Rest/JsonContent.cs +++ b/src/SignhostAPIClient/Rest/JsonContent.cs @@ -5,16 +5,16 @@ namespace Signhost.APIClient.Rest { /// - /// Helper class + /// See the helper class. /// internal static class JsonContent { /// /// Creates a new . /// - /// Type to serialize - /// Value to serialize - /// + /// Type to serialize. + /// Value to serialize. + /// . internal static JsonContent From(T value) { return new JsonContent(value); @@ -31,7 +31,7 @@ internal class JsonContent /// /// Initializes a new instance of the class. /// - /// Value to serialize + /// Value to serialize. public JsonContent(T value) : base(ToJson(value)) { diff --git a/src/SignhostAPIClient/Rest/SignHostApiClient.cs b/src/SignhostAPIClient/Rest/SignHostApiClient.cs index 780aa1ba..a453f2cb 100644 --- a/src/SignhostAPIClient/Rest/SignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/SignHostApiClient.cs @@ -35,7 +35,7 @@ public class SignhostApiClient /// Initializes a new instance of the class. /// Set your usertoken and APPKey by creating a . /// - /// + /// . public SignhostApiClient(ISignhostApiClientSettings settings) : this(settings, new HttpClient()) { @@ -45,7 +45,7 @@ public SignhostApiClient(ISignhostApiClientSettings settings) /// Initializes a new instance of the class. /// Set your usertoken and APPKey by creating a . /// - /// + /// . /// to use for all http calls. public SignhostApiClient( ISignhostApiClientSettings settings, @@ -153,6 +153,7 @@ public async Task GetTransactionAsync( return response.Value; } + /// public async Task DeleteTransactionAsync( string transactionId, CancellationToken cancellationToken = default) diff --git a/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs b/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs index bdffe930..da6c1899 100644 --- a/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs +++ b/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs @@ -22,7 +22,9 @@ public class SignhostApiReceiver /// Initializes a new instance of the class. /// Set your SharedSecret by creating a . /// - /// + /// + /// Settings for the receiver. + /// public SignhostApiReceiver(SignhostApiReceiverSettings receiverSettings) { this.settings = receiverSettings; @@ -64,10 +66,12 @@ private string CalculateChecksumFromPostback(PostbackTransaction postback) } } - private PostbackTransaction DeserializeToPostbackTransaction(string body) - { - return JsonSerializer.Deserialize(body, SignhostJsonSerializerOptions.Default); - } private string GetChecksumFromHeadersOrPostback( + private PostbackTransaction DeserializeToPostbackTransaction(string body) + { + return JsonSerializer.Deserialize(body, SignhostJsonSerializerOptions.Default); + } + + private string GetChecksumFromHeadersOrPostback( IDictionary headers, PostbackTransaction postback) { diff --git a/src/signhost.ruleset b/src/signhost.ruleset index 6d703aa7..51c9a98e 100644 --- a/src/signhost.ruleset +++ b/src/signhost.ruleset @@ -6,7 +6,11 @@ + + + + From b010f6113e78a3ef24be9b3cb06b5c0601590121 Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Thu, 27 Nov 2025 19:43:57 +0100 Subject: [PATCH 06/22] Remove obsolete exception --- .../Rest/ErrorHandling/SignhostException.cs | 36 ------------------- 1 file changed, 36 deletions(-) delete mode 100644 src/SignhostAPIClient/Rest/ErrorHandling/SignhostException.cs diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/SignhostException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/SignhostException.cs deleted file mode 100644 index 2e5db3d0..00000000 --- a/src/SignhostAPIClient/Rest/ErrorHandling/SignhostException.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; - -namespace Signhost.APIClient.Rest.ErrorHandling -{ - // TO-DO: Remove in v5 - [Serializable] - [Obsolete("Unused will be removed")] - public class SignhostException - : SignhostRestApiClientException - { - public SignhostException() - : base() - { - } - - public SignhostException(string message) - : base(message) - { - } - - public SignhostException(string message, Exception innerException) - : base(message, innerException) - { - HelpLink = "https://api.signhost.com/Help"; - } - -#if SERIALIZABLE - protected SignhostException( - SerializationInfo info, - StreamingContext context) - : base(info, context) - { - } -#endif - } -} From 9114954d2b46b3b9cfec0c8f703d9823124de021 Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Fri, 21 Nov 2025 19:07:22 +0100 Subject: [PATCH 07/22] Reformat use file-scoped namespaces --- .../LevelEnumConverterTests.cs | 103 +- src/SignhostAPIClient.Tests/PostbackTests.cs | 209 ++-- .../SignhostApiClientTests.cs | 1003 ++++++++--------- .../SignhostApiReceiverTests.cs | 115 +- src/SignhostAPIClient/Rest/ApiResponse.cs | 49 +- .../Rest/DataObjects/Activity.cs | 19 +- .../Rest/DataObjects/ActivityType.cs | 265 +++-- .../Rest/DataObjects/ConsentVerification.cs | 15 +- .../Rest/DataObjects/CscVerification.cs | 51 +- .../DataObjects/DeleteTransactionOptions.cs | 25 +- .../Rest/DataObjects/DigidVerification.cs | 13 +- .../DataObjects/EherkenningVerification.cs | 25 +- .../DataObjects/EidasLoginVerification.cs | 61 +- .../Rest/DataObjects/Field.cs | 17 +- .../Rest/DataObjects/FileEntry.cs | 11 +- .../Rest/DataObjects/FileLink.cs | 13 +- .../Rest/DataObjects/FileMeta.cs | 33 +- .../Rest/DataObjects/FileSignerMeta.cs | 9 +- .../Rest/DataObjects/IPAddressVerification.cs | 19 +- .../Rest/DataObjects/IVerification.cs | 38 +- .../Rest/DataObjects/IdealVerification.cs | 15 +- .../Rest/DataObjects/IdinVerification.cs | 19 +- .../ItsmeIdentificationVerification.cs | 19 +- .../Rest/DataObjects/Level.cs | 41 +- .../Rest/DataObjects/Location.cs | 25 +- .../Rest/DataObjects/OidcVerification.cs | 19 +- .../Rest/DataObjects/OnfidoVerification.cs | 43 +- .../DataObjects/PhoneNumberVerification.cs | 13 +- .../Rest/DataObjects/PostbackTransaction.cs | 13 +- .../Rest/DataObjects/Receiver.cs | 45 +- .../Rest/DataObjects/ScribbleVerification.cs | 15 +- .../Rest/DataObjects/Signer.cs | 91 +- .../Rest/DataObjects/SurfnetVerification.cs | 13 +- .../Rest/DataObjects/Transaction.cs | 65 +- .../Rest/DataObjects/TransactionStatus.cs | 89 +- .../BadAuthorizationException.cs | 53 +- .../Rest/ErrorHandling/BadRequestException.cs | 51 +- .../ErrorHandling/DefaultSignhostException.cs | 41 +- .../Rest/ErrorHandling/GoneException.cs | 95 +- ...pResponseMessageErrorHandlingExtensions.cs | 213 ++-- .../InternalServerErrorException.cs | 63 +- .../Rest/ErrorHandling/NotFoundException.cs | 47 +- .../ErrorHandling/OutOfCreditsException.cs | 25 +- .../SignhostRestApiClientException.cs | 47 +- .../Rest/FileDigestOptions.cs | 41 +- .../Rest/FileUploadOptions.cs | 19 +- .../Rest/HttpContentJsonExtensions.cs | 39 +- .../Rest/ISignHostApiClient.cs | 483 ++++---- .../Rest/ISignhostApiClientSettings.cs | 37 +- .../Rest/ISignhostApiReceiver.cs | 31 +- src/SignhostAPIClient/Rest/JsonContent.cs | 59 +- .../Rest/JsonConverters/LevelEnumConverter.cs | 77 +- .../Rest/SignHostApiClient.cs | 809 +++++++------ .../Rest/SignHostApiClientSettings.cs | 37 +- .../Rest/SignhostApiReceiver.cs | 131 ++- .../Rest/SignhostApiReceiverSettings.cs | 29 +- .../Rest/SignhostJsonSerializerOptions.cs | 29 +- .../StreamContentDigestOptionsExtensions.cs | 135 ++- .../Rest/UriPathExtensions.cs | 31 +- 59 files changed, 2590 insertions(+), 2650 deletions(-) diff --git a/src/SignhostAPIClient.Tests/LevelEnumConverterTests.cs b/src/SignhostAPIClient.Tests/LevelEnumConverterTests.cs index 534d5484..92ca645a 100644 --- a/src/SignhostAPIClient.Tests/LevelEnumConverterTests.cs +++ b/src/SignhostAPIClient.Tests/LevelEnumConverterTests.cs @@ -6,74 +6,73 @@ using System.Collections.Generic; using Xunit; -namespace Signhost.APIClient.Rest.Tests +namespace Signhost.APIClient.Rest.Tests; + +public class LevelEnumConverterTests { - public class LevelEnumConverterTests + [Fact] + public void when_Level_is_null_should_deserialize_to_null() { - [Fact] - public void when_Level_is_null_should_deserialize_to_null() - { - // Arrange - const string json = "{\"Type\":\"eIDAS Login\",\"Level\":null}"; + // Arrange + const string json = "{\"Type\":\"eIDAS Login\",\"Level\":null}"; - // Act - var eidasLogin = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); + // Act + var eidasLogin = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); - // Assert - eidasLogin.Level.Should().Be(null); - } + // Assert + eidasLogin.Level.Should().Be(null); + } - [Fact] - public void when_Level_is_not_supplied_should_deserialize_to_null() - { - // Arrange - const string json = "{\"Type\":\"eIDAS Login\"}"; + [Fact] + public void when_Level_is_not_supplied_should_deserialize_to_null() + { + // Arrange + const string json = "{\"Type\":\"eIDAS Login\"}"; - // Act - var eidasLogin = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); + // Act + var eidasLogin = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); - // Assert - eidasLogin.Level.Should().Be(null); - } + // Assert + eidasLogin.Level.Should().Be(null); + } - [Fact] - public void when_Level_is_unknown_should_deserialize_to_Unknown_Level() - { - // Arrange - const string json = "{\"Type\":\"eIDAS Login\",\"Level\":\"foobar\"}"; + [Fact] + public void when_Level_is_unknown_should_deserialize_to_Unknown_Level() + { + // Arrange + const string json = "{\"Type\":\"eIDAS Login\",\"Level\":\"foobar\"}"; - // Act - var eidasLogin = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); + // Act + var eidasLogin = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); - // Assert - eidasLogin.Level.Should().Be(Level.Unknown); - } + // Assert + eidasLogin.Level.Should().Be(Level.Unknown); + } - [Theory] - [ClassData(typeof(LevelTestData))] - public void when_Level_is_valid_should_deserialize_to_correct_value(Level level) - { - // Arrange - string json = $"{{\"Type\":\"eIDAS Login\",\"Level\":\"{level}\"}}"; + [Theory] + [ClassData(typeof(LevelTestData))] + public void when_Level_is_valid_should_deserialize_to_correct_value(Level level) + { + // Arrange + string json = $"{{\"Type\":\"eIDAS Login\",\"Level\":\"{level}\"}}"; - // Act - var eidasLogin = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); + // Act + var eidasLogin = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); - // Assert - eidasLogin.Level.Should().Be(level); - } + // Assert + eidasLogin.Level.Should().Be(level); + } - private class LevelTestData - : IEnumerable + private class LevelTestData + : IEnumerable + { + public IEnumerator GetEnumerator() { - public IEnumerator GetEnumerator() - { - foreach (var value in Enum.GetValues(typeof(Level))) { - yield return new[] { value }; - } + foreach (var value in Enum.GetValues(typeof(Level))) { + yield return new[] { value }; } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/src/SignhostAPIClient.Tests/PostbackTests.cs b/src/SignhostAPIClient.Tests/PostbackTests.cs index 7a005953..8d3314cd 100644 --- a/src/SignhostAPIClient.Tests/PostbackTests.cs +++ b/src/SignhostAPIClient.Tests/PostbackTests.cs @@ -6,112 +6,111 @@ using SignhostAPIClient.Tests.JSON; using Xunit; -namespace Signhost.APIClient.Rest.Tests +namespace Signhost.APIClient.Rest.Tests; + +public class PostbackTests { - public class PostbackTests + [Fact] + public void PostbackTransaction_should_get_serialized_correctly() { - [Fact] - public void PostbackTransaction_should_get_serialized_correctly() - { - string json = JsonResources.MockPostbackValid; - var postbackTransaction = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); - - postbackTransaction.Id .Should().Be("b10ae331-af78-4e79-a39e-5b64693b6b68"); - postbackTransaction.Status .Should().Be(TransactionStatus.InProgress); - postbackTransaction.Seal .Should().BeTrue(); - postbackTransaction.Reference .Should().Be("Contract #123"); - postbackTransaction.PostbackUrl .Should().Be("https://example.com/postback.php"); - postbackTransaction.SignRequestMode .Should().Be(2); - postbackTransaction.DaysToExpire .Should().Be(30); - postbackTransaction.SendEmailNotifications.Should().BeTrue(); - postbackTransaction.CreatedDateTime .Should().Be(DateTimeOffset.Parse("2016-08-31T21:22:56.2467731+02:00")); - postbackTransaction.CanceledDateTime .Should().BeNull(); - (postbackTransaction.Context is null) .Should().BeTrue(); - postbackTransaction.Checksum .Should().Be("cdc09eee2ed6df2846dcc193aedfef59f2834f8d"); - - var signers = postbackTransaction.Signers; - signers.Should().HaveCount(1); - - var signer = signers.Single(); - signer.Id .Should().Be("fa95495d-6c59-48e0-962a-a4552f8d6b85"); - signer.Expires .Should().BeNull(); - signer.Email .Should().Be("user@example.com"); - signer.SendSignRequest .Should().BeTrue(); - signer.SendSignConfirmation.Should().BeNull(); - signer.SignRequestMessage .Should().Be("Hello, could you please sign this document? Best regards, John Doe"); - signer.DaysToRemind .Should().Be(15); - signer.Language .Should().Be("en-US"); - signer.ScribbleName .Should().Be("John Doe"); - signer.ScribbleNameFixed .Should().BeFalse(); - signer.Reference .Should().Be("Client #123"); - signer.ReturnUrl .Should().Be("https://signhost.com"); - signer.RejectReason .Should().BeNull(); - signer.SignUrl .Should().Be("https://view.signhost.com/sign/d3c93bd6-f1ce-48e7-8c9c-c2babfdd4034"); - (signer.Context is null) .Should().BeTrue(); - - var verifications = signer.Verifications; - verifications.Should().HaveCount(3); - - var phoneNumberVerification = verifications[0] as PhoneNumberVerification; - phoneNumberVerification .Should().NotBeNull(); - phoneNumberVerification.Number.Should().Be("+31612345678"); - - var scribbleVerification = verifications[1] as ScribbleVerification; - scribbleVerification .Should().NotBeNull(); - scribbleVerification.RequireHandsignature.Should().BeFalse(); - scribbleVerification.ScribbleNameFixed .Should().BeFalse(); - scribbleVerification.ScribbleName .Should().Be("John Doe"); - - var ipAddressVerification = verifications[2] as IPAddressVerification; - ipAddressVerification .Should().NotBeNull(); - ipAddressVerification.IPAddress.Should().Be("1.2.3.4"); - - var activities = signer.Activities; - activities.Should().HaveCount(3); - - var openedActivity = activities[0]; - openedActivity.Id .Should().Be("bcba44a9-c201-4494-9920-2c1f7baebcf0"); - openedActivity.Code .Should().Be(ActivityType.Opened); - openedActivity.Info .Should().BeNull(); - openedActivity.CreatedDateTime.Should().Be(DateTimeOffset.Parse("2016-06-15T23:33:04.1965465+02:00")); - - var documentOpenedActivity = activities[1]; - documentOpenedActivity.Id .Should().Be("7aacf96a-5c2f-475d-98a5-726e41bfc5d3"); - documentOpenedActivity.Code .Should().Be(ActivityType.DocumentOpened); - documentOpenedActivity.Info .Should().Be("file1"); - documentOpenedActivity.CreatedDateTime.Should().Be(DateTimeOffset.Parse("2020-01-30T16:31:05.6679583+01:00")); - - var signedActivity = activities[2]; - signedActivity.Id .Should().Be("de94cf6e-e1a3-4c33-93bf-2013b036daaf"); - signedActivity.Code .Should().Be(ActivityType.Signed); - signedActivity.Info .Should().BeNull(); - signedActivity.CreatedDateTime.Should().Be(DateTimeOffset.Parse("2016-06-15T23:38:04.1965465+02:00")); - - var receivers = postbackTransaction.Receivers; - receivers.Should().HaveCount(1); - - var receiver = receivers.Single(); - receiver.Name .Should().Be("John Doe"); - receiver.Email .Should().Be("user@example.com"); - receiver.Language .Should().Be("en-US"); - receiver.Message .Should().Be("Hello, please find enclosed the digital signed document. Best regards, John Doe"); - receiver.Reference .Should().BeNull(); - receiver.Activities .Should().BeNull(); - (receiver.Context is null).Should().BeTrue(); - - var files = postbackTransaction.Files; - files.Should().HaveCount(1); - - var file = files["file1"]; - file.DisplayName.Should().Be("Sample File"); - - var links = file.Links; - links.Should().HaveCount(1); - - var link = links.Single(); - link.Rel .Should().Be("file"); - link.Type.Should().Be("application/pdf"); - link.Link.Should().Be("https://api.signhost.com/api/transaction/b10ae331-af78-4e79-a39e-5b64693b6b68/file/file1"); - } + string json = JsonResources.MockPostbackValid; + var postbackTransaction = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); + + postbackTransaction.Id .Should().Be("b10ae331-af78-4e79-a39e-5b64693b6b68"); + postbackTransaction.Status .Should().Be(TransactionStatus.InProgress); + postbackTransaction.Seal .Should().BeTrue(); + postbackTransaction.Reference .Should().Be("Contract #123"); + postbackTransaction.PostbackUrl .Should().Be("https://example.com/postback.php"); + postbackTransaction.SignRequestMode .Should().Be(2); + postbackTransaction.DaysToExpire .Should().Be(30); + postbackTransaction.SendEmailNotifications.Should().BeTrue(); + postbackTransaction.CreatedDateTime .Should().Be(DateTimeOffset.Parse("2016-08-31T21:22:56.2467731+02:00")); + postbackTransaction.CanceledDateTime .Should().BeNull(); + (postbackTransaction.Context is null) .Should().BeTrue(); + postbackTransaction.Checksum .Should().Be("cdc09eee2ed6df2846dcc193aedfef59f2834f8d"); + + var signers = postbackTransaction.Signers; + signers.Should().HaveCount(1); + + var signer = signers.Single(); + signer.Id .Should().Be("fa95495d-6c59-48e0-962a-a4552f8d6b85"); + signer.Expires .Should().BeNull(); + signer.Email .Should().Be("user@example.com"); + signer.SendSignRequest .Should().BeTrue(); + signer.SendSignConfirmation.Should().BeNull(); + signer.SignRequestMessage .Should().Be("Hello, could you please sign this document? Best regards, John Doe"); + signer.DaysToRemind .Should().Be(15); + signer.Language .Should().Be("en-US"); + signer.ScribbleName .Should().Be("John Doe"); + signer.ScribbleNameFixed .Should().BeFalse(); + signer.Reference .Should().Be("Client #123"); + signer.ReturnUrl .Should().Be("https://signhost.com"); + signer.RejectReason .Should().BeNull(); + signer.SignUrl .Should().Be("https://view.signhost.com/sign/d3c93bd6-f1ce-48e7-8c9c-c2babfdd4034"); + (signer.Context is null) .Should().BeTrue(); + + var verifications = signer.Verifications; + verifications.Should().HaveCount(3); + + var phoneNumberVerification = verifications[0] as PhoneNumberVerification; + phoneNumberVerification .Should().NotBeNull(); + phoneNumberVerification.Number.Should().Be("+31612345678"); + + var scribbleVerification = verifications[1] as ScribbleVerification; + scribbleVerification .Should().NotBeNull(); + scribbleVerification.RequireHandsignature.Should().BeFalse(); + scribbleVerification.ScribbleNameFixed .Should().BeFalse(); + scribbleVerification.ScribbleName .Should().Be("John Doe"); + + var ipAddressVerification = verifications[2] as IPAddressVerification; + ipAddressVerification .Should().NotBeNull(); + ipAddressVerification.IPAddress.Should().Be("1.2.3.4"); + + var activities = signer.Activities; + activities.Should().HaveCount(3); + + var openedActivity = activities[0]; + openedActivity.Id .Should().Be("bcba44a9-c201-4494-9920-2c1f7baebcf0"); + openedActivity.Code .Should().Be(ActivityType.Opened); + openedActivity.Info .Should().BeNull(); + openedActivity.CreatedDateTime.Should().Be(DateTimeOffset.Parse("2016-06-15T23:33:04.1965465+02:00")); + + var documentOpenedActivity = activities[1]; + documentOpenedActivity.Id .Should().Be("7aacf96a-5c2f-475d-98a5-726e41bfc5d3"); + documentOpenedActivity.Code .Should().Be(ActivityType.DocumentOpened); + documentOpenedActivity.Info .Should().Be("file1"); + documentOpenedActivity.CreatedDateTime.Should().Be(DateTimeOffset.Parse("2020-01-30T16:31:05.6679583+01:00")); + + var signedActivity = activities[2]; + signedActivity.Id .Should().Be("de94cf6e-e1a3-4c33-93bf-2013b036daaf"); + signedActivity.Code .Should().Be(ActivityType.Signed); + signedActivity.Info .Should().BeNull(); + signedActivity.CreatedDateTime.Should().Be(DateTimeOffset.Parse("2016-06-15T23:38:04.1965465+02:00")); + + var receivers = postbackTransaction.Receivers; + receivers.Should().HaveCount(1); + + var receiver = receivers.Single(); + receiver.Name .Should().Be("John Doe"); + receiver.Email .Should().Be("user@example.com"); + receiver.Language .Should().Be("en-US"); + receiver.Message .Should().Be("Hello, please find enclosed the digital signed document. Best regards, John Doe"); + receiver.Reference .Should().BeNull(); + receiver.Activities .Should().BeNull(); + (receiver.Context is null).Should().BeTrue(); + + var files = postbackTransaction.Files; + files.Should().HaveCount(1); + + var file = files["file1"]; + file.DisplayName.Should().Be("Sample File"); + + var links = file.Links; + links.Should().HaveCount(1); + + var link = links.Single(); + link.Rel .Should().Be("file"); + link.Type.Should().Be("application/pdf"); + link.Link.Should().Be("https://api.signhost.com/api/transaction/b10ae331-af78-4e79-a39e-5b64693b6b68/file/file1"); } } diff --git a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs index fc3c085a..aee6ccff 100644 --- a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs +++ b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs @@ -10,661 +10,660 @@ using System.Net; using SignhostAPIClient.Tests.JSON; -namespace Signhost.APIClient.Rest.Tests +namespace Signhost.APIClient.Rest.Tests; + +public class SignhostApiClientTests { - public class SignhostApiClientTests - { - private readonly SignhostApiClientSettings settings = new("AppKey", "Usertoken") { - Endpoint = "http://localhost/api/" - }; + private readonly SignhostApiClientSettings settings = new("AppKey", "Usertoken") { + Endpoint = "http://localhost/api/" + }; - private readonly SignhostApiClientSettings oauthSettings = new("AppKey") { - Endpoint = "http://localhost/api/" - }; + private readonly SignhostApiClientSettings oauthSettings = new("AppKey") { + Endpoint = "http://localhost/api/" + }; - [Fact] - public async Task when_AddOrReplaceFileMetaToTransaction_is_called_then_the_request_body_should_contain_the_serialized_file_meta() - { - var mockHttp = new MockHttpMessageHandler(); + [Fact] + public async Task when_AddOrReplaceFileMetaToTransaction_is_called_then_the_request_body_should_contain_the_serialized_file_meta() + { + var mockHttp = new MockHttpMessageHandler(); - mockHttp.Expect(HttpMethod.Put, "http://localhost/api/transaction/transactionId/file/fileId") - .WithContent(JsonResources.AddOrReplaceFileMetaToTransaction) - .Respond(HttpStatusCode.OK); + mockHttp.Expect(HttpMethod.Put, "http://localhost/api/transaction/transactionId/file/fileId") + .WithContent(JsonResources.AddOrReplaceFileMetaToTransaction) + .Respond(HttpStatusCode.OK); - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using (var httpClient = mockHttp.ToHttpClient()) { + var signhostApiClient = new SignhostApiClient(settings, httpClient); - var fileSignerMeta = new FileSignerMeta - { - FormSets = new string[] { "SampleFormSet" } - }; + var fileSignerMeta = new FileSignerMeta + { + FormSets = new string[] { "SampleFormSet" } + }; - var field = new Field + var field = new Field + { + Type = "Check", + Value = "I agree", + Location = new Location { - Type = "Check", - Value = "I agree", - Location = new Location - { - Search = "test" - } - }; + Search = "test" + } + }; - FileMeta fileMeta = new FileMeta + FileMeta fileMeta = new FileMeta + { + Signers = new Dictionary { - Signers = new Dictionary - { - { "someSignerId", fileSignerMeta } - }, - FormSets = new Dictionary> - { - { "SampleFormSet", new Dictionary - { - { "SampleCheck", field } - } + { "someSignerId", fileSignerMeta } + }, + FormSets = new Dictionary> + { + { "SampleFormSet", new Dictionary + { + { "SampleCheck", field } } } - }; - - await signhostApiClient.AddOrReplaceFileMetaToTransactionAsync(fileMeta, "transactionId", "fileId"); - } + } + }; - mockHttp.VerifyNoOutstandingExpectation(); + await signhostApiClient.AddOrReplaceFileMetaToTransactionAsync(fileMeta, "transactionId", "fileId"); } - [Fact] - public async Task when_a_GetTransaction_is_called_then_we_should_have_called_the_transaction_get_once() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.OK, new StringContent(JsonResources.GetTransaction)); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { + [Fact] + public async Task when_a_GetTransaction_is_called_then_we_should_have_called_the_transaction_get_once() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Respond(HttpStatusCode.OK, new StringContent(JsonResources.GetTransaction)); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using (var httpClient = mockHttp.ToHttpClient()) { - var result = await signhostApiClient.GetTransactionAsync("transaction Id"); - result.Id.Should().Be("c487be92-0255-40c7-bd7d-20805a65e7d9"); - } + var signhostApiClient = new SignhostApiClient(settings, httpClient); - mockHttp.VerifyNoOutstandingExpectation(); + var result = await signhostApiClient.GetTransactionAsync("transaction Id"); + result.Id.Should().Be("c487be92-0255-40c7-bd7d-20805a65e7d9"); } - [Fact] - public async Task when_GetTransaction_is_called_and_the_authorization_is_bad_then_we_should_get_a_BadAuthorizationException() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.Unauthorized, new StringContent(""" - { - "message": "unauthorized" - } - """)); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { + [Fact] + public async Task when_GetTransaction_is_called_and_the_authorization_is_bad_then_we_should_get_a_BadAuthorizationException() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Respond(HttpStatusCode.Unauthorized, new StringContent(""" + { + "message": "unauthorized" + } + """)); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using (var httpClient = mockHttp.ToHttpClient()) { - Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - await getTransaction.Should().ThrowAsync(); - } + var signhostApiClient = new SignhostApiClient(settings, httpClient); - mockHttp.VerifyNoOutstandingExpectation(); + Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); + await getTransaction.Should().ThrowAsync(); } - [Fact] - public async Task when_GetTransaction_is_called_and_request_is_bad_then_we_should_get_a_BadRequestException() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.BadRequest, new StringContent(""" - { - "message": "Bad Request" - } - """)); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { + [Fact] + public async Task when_GetTransaction_is_called_and_request_is_bad_then_we_should_get_a_BadRequestException() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Respond(HttpStatusCode.BadRequest, new StringContent(""" + { + "message": "Bad Request" + } + """)); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using (var httpClient = mockHttp.ToHttpClient()) { - Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - await getTransaction.Should().ThrowAsync(); - } + var signhostApiClient = new SignhostApiClient(settings, httpClient); - mockHttp.VerifyNoOutstandingExpectation(); + Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); + await getTransaction.Should().ThrowAsync(); } - [Fact] - public async Task when_GetTransaction_is_called_and_credits_have_run_out_then_we_should_get_a_OutOfCreditsException() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.PaymentRequired, new StringContent(""" - { - "type": "https://api.signhost.com/problem/subscription/out-of-credits" - } - """)); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { + [Fact] + public async Task when_GetTransaction_is_called_and_credits_have_run_out_then_we_should_get_a_OutOfCreditsException() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Respond(HttpStatusCode.PaymentRequired, new StringContent(""" + { + "type": "https://api.signhost.com/problem/subscription/out-of-credits" + } + """)); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using (var httpClient = mockHttp.ToHttpClient()) { - Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - await getTransaction.Should().ThrowAsync(); - } + var signhostApiClient = new SignhostApiClient(settings, httpClient); - mockHttp.VerifyNoOutstandingExpectation(); + Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); + await getTransaction.Should().ThrowAsync(); } - [Fact] - public async Task when_GetTransaction_is_called_and_not_found_then_we_should_get_a_NotFoundException() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.NotFound, new StringContent(""" - { - "message": "Not Found" - } - """)); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { + [Fact] + public async Task when_GetTransaction_is_called_and_not_found_then_we_should_get_a_NotFoundException() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Respond(HttpStatusCode.NotFound, new StringContent(""" + { + "message": "Not Found" + } + """)); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using (var httpClient = mockHttp.ToHttpClient()) { - Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); + var signhostApiClient = new SignhostApiClient(settings, httpClient); - await getTransaction.Should().ThrowAsync().WithMessage("Not Found"); - } + Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - mockHttp.VerifyNoOutstandingExpectation(); + await getTransaction.Should().ThrowAsync().WithMessage("Not Found"); } - [Fact] - public async Task when_GetTransaction_is_called_and_unkownerror_like_418_occures_then_we_should_get_a_SignhostException() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond((HttpStatusCode)418, new StringContent(""" - { - "message": "418 I'm a teapot" - } - """)); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { + [Fact] + public async Task when_GetTransaction_is_called_and_unkownerror_like_418_occures_then_we_should_get_a_SignhostException() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Respond((HttpStatusCode)418, new StringContent(""" + { + "message": "418 I'm a teapot" + } + """)); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using (var httpClient = mockHttp.ToHttpClient()) { - Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - await getTransaction.Should().ThrowAsync() - .WithMessage("*418*"); - } + var signhostApiClient = new SignhostApiClient(settings, httpClient); - mockHttp.VerifyNoOutstandingExpectation(); + Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); + await getTransaction.Should().ThrowAsync() + .WithMessage("*418*"); } - [Fact] - public async Task when_GetTransaction_is_called_and_there_is_an_InternalServerError_then_we_should_get_a_InternalServerErrorException() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.InternalServerError, new StringContent(""" - { - "message": "Internal Server Error" - } - """)); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { + [Fact] + public async Task when_GetTransaction_is_called_and_there_is_an_InternalServerError_then_we_should_get_a_InternalServerErrorException() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Respond(HttpStatusCode.InternalServerError, new StringContent(""" + { + "message": "Internal Server Error" + } + """)); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using (var httpClient = mockHttp.ToHttpClient()) { - Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - await getTransaction.Should().ThrowAsync(); - } + var signhostApiClient = new SignhostApiClient(settings, httpClient); - mockHttp.VerifyNoOutstandingExpectation(); + Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); + await getTransaction.Should().ThrowAsync(); } - [Fact] - public async Task When_GetTransaction_is_called_on_gone_transaction_we_shoud_get_a_GoneException() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.Gone, new StringContent(JsonResources.GetTransaction)); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + [Fact] + public async Task When_GetTransaction_is_called_on_gone_transaction_we_shoud_get_a_GoneException() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Respond(HttpStatusCode.Gone, new StringContent(JsonResources.GetTransaction)); - Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - await getTransaction.Should().ThrowAsync>(); - } + using (var httpClient = mockHttp.ToHttpClient()) { + var signhostApiClient = new SignhostApiClient(settings, httpClient); - mockHttp.VerifyNoOutstandingExpectation(); + Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); + await getTransaction.Should().ThrowAsync>(); } - [Fact] - public async Task When_GetTransaction_is_called_and_gone_is_expected_we_should_get_a_transaction() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.Gone, new StringContent(JsonResources.GetTransaction)); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + [Fact] + public async Task When_GetTransaction_is_called_and_gone_is_expected_we_should_get_a_transaction() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Respond(HttpStatusCode.Gone, new StringContent(JsonResources.GetTransaction)); - Func getTransaction = () => signhostApiClient.GetTransactionResponseAsync("transaction Id"); - await getTransaction.Should().NotThrowAsync(); - } + using (var httpClient = mockHttp.ToHttpClient()) { + var signhostApiClient = new SignhostApiClient(settings, httpClient); - mockHttp.VerifyNoOutstandingExpectation(); + Func getTransaction = () => signhostApiClient.GetTransactionResponseAsync("transaction Id"); + await getTransaction.Should().NotThrowAsync(); } - [Fact] - public async Task when_a_CreateTransaction_is_called_then_we_should_have_called_the_transaction_Post_once() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Post, "http://localhost/api/transaction") - .Respond(HttpStatusCode.OK, new StringContent(JsonResources.AddTransaction)); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + [Fact] + public async Task when_a_CreateTransaction_is_called_then_we_should_have_called_the_transaction_Post_once() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Post, "http://localhost/api/transaction") + .Respond(HttpStatusCode.OK, new StringContent(JsonResources.AddTransaction)); - Signer testSigner = new Signer(); - testSigner.Email = "firstname.lastname@gmail.com"; + using (var httpClient = mockHttp.ToHttpClient()) { + var signhostApiClient = new SignhostApiClient(settings, httpClient); - Transaction testTransaction = new Transaction(); - testTransaction.Signers.Add(testSigner); + Signer testSigner = new Signer(); + testSigner.Email = "firstname.lastname@gmail.com"; - var result = await signhostApiClient.CreateTransactionAsync(testTransaction); - result.Id.Should().Be("c487be92-0255-40c7-bd7d-20805a65e7d9"); - } + Transaction testTransaction = new Transaction(); + testTransaction.Signers.Add(testSigner); - mockHttp.VerifyNoOutstandingExpectation(); + var result = await signhostApiClient.CreateTransactionAsync(testTransaction); + result.Id.Should().Be("c487be92-0255-40c7-bd7d-20805a65e7d9"); } - [Fact] - public async Task when_a_CreateTransaction_is_called_we_can_add_custom_http_headers() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Post, "http://localhost/api/transaction") - .WithHeaders("X-Forwarded-For", "localhost") - .With(matcher => matcher.Headers.UserAgent.ToString().Contains("SignhostClientLibrary")) - .Respond(HttpStatusCode.OK, new StringContent(JsonResources.AddTransaction)); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { - settings.AddHeader = (AddHeaders a) => a("X-Forwarded-For", "localhost"); + [Fact] + public async Task when_a_CreateTransaction_is_called_we_can_add_custom_http_headers() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Post, "http://localhost/api/transaction") + .WithHeaders("X-Forwarded-For", "localhost") + .With(matcher => matcher.Headers.UserAgent.ToString().Contains("SignhostClientLibrary")) + .Respond(HttpStatusCode.OK, new StringContent(JsonResources.AddTransaction)); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using (var httpClient = mockHttp.ToHttpClient()) { + settings.AddHeader = (AddHeaders a) => a("X-Forwarded-For", "localhost"); - Transaction testTransaction = new Transaction(); + var signhostApiClient = new SignhostApiClient(settings, httpClient); - var result = await signhostApiClient.CreateTransactionAsync(testTransaction); - } + Transaction testTransaction = new Transaction(); - mockHttp.VerifyNoOutstandingExpectation(); + var result = await signhostApiClient.CreateTransactionAsync(testTransaction); } - [Fact] - public async Task when_CreateTransaction_is_called_with_invalid_email_then_we_should_get_a_BadRequestException() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Post, "http://localhost/api/transaction") - .WithHeaders("Content-Type", "application/json") - .Respond(HttpStatusCode.BadRequest, new StringContent(""" - { - "message": "Bad Request" - } - """)); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + [Fact] + public async Task when_CreateTransaction_is_called_with_invalid_email_then_we_should_get_a_BadRequestException() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Post, "http://localhost/api/transaction") + .WithHeaders("Content-Type", "application/json") + .Respond(HttpStatusCode.BadRequest, new StringContent(""" + { + "message": "Bad Request" + } + """)); - Signer testSigner = new Signer(); - testSigner.Email = "firstname.lastnamegmail.com"; + using (var httpClient = mockHttp.ToHttpClient()) { + var signhostApiClient = new SignhostApiClient(settings, httpClient); - Transaction testTransaction = new Transaction(); - testTransaction.Signers.Add(testSigner); + Signer testSigner = new Signer(); + testSigner.Email = "firstname.lastnamegmail.com"; - Func getTransaction = () => signhostApiClient.CreateTransactionAsync(testTransaction); - await getTransaction.Should().ThrowAsync(); - } + Transaction testTransaction = new Transaction(); + testTransaction.Signers.Add(testSigner); - mockHttp.VerifyNoOutstandingExpectation(); + Func getTransaction = () => signhostApiClient.CreateTransactionAsync(testTransaction); + await getTransaction.Should().ThrowAsync(); } - [Fact] - public async Task when_a_function_is_called_with_a_wrong_endpoint_we_should_get_a_SignhostRestApiClientException() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.BadGateway, new StringContent(""" - { - "message": "Bad Gateway" - } - """)); - - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + mockHttp.VerifyNoOutstandingExpectation(); + } - Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - await getTransaction.Should().ThrowAsync() - .WithMessage("Bad Gateway"); + [Fact] + public async Task when_a_function_is_called_with_a_wrong_endpoint_we_should_get_a_SignhostRestApiClientException() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Respond(HttpStatusCode.BadGateway, new StringContent(""" + { + "message": "Bad Gateway" } + """)); - mockHttp.VerifyNoOutstandingExpectation(); + using (var httpClient = mockHttp.ToHttpClient()) { + var signhostApiClient = new SignhostApiClient(settings, httpClient); + + Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); + await getTransaction.Should().ThrowAsync() + .WithMessage("Bad Gateway"); } - [Fact] - public async Task when_a_DeleteTransaction_is_called_then_we_should_have_called_the_transaction_delete_once() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Delete, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.OK, new StringContent(JsonResources.DeleteTransaction)); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + [Fact] + public async Task when_a_DeleteTransaction_is_called_then_we_should_have_called_the_transaction_delete_once() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Delete, "http://localhost/api/transaction/transaction Id") + .Respond(HttpStatusCode.OK, new StringContent(JsonResources.DeleteTransaction)); - await signhostApiClient.DeleteTransactionAsync("transaction Id"); - } + using (var httpClient = mockHttp.ToHttpClient()) { + var signhostApiClient = new SignhostApiClient(settings, httpClient); - mockHttp.VerifyNoOutstandingExpectation(); + await signhostApiClient.DeleteTransactionAsync("transaction Id"); } - [Fact] - public async Task when_a_DeleteTransaction_with_notification_is_called_then_we_should_have_called_the_transaction_delete_once() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Delete, "http://localhost/api/transaction/transaction Id") - .WithHeaders("Content-Type", "application/json") - //.With(matcher => matcher.Content.ToString().Contains("'SendNotifications': true")) - .Respond(HttpStatusCode.OK, new StringContent(JsonResources.DeleteTransaction)); - - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); - - await signhostApiClient.DeleteTransactionAsync( - "transaction Id", - new DeleteTransactionOptions { SendNotifications = true }); - } + mockHttp.VerifyNoOutstandingExpectation(); + } - mockHttp.VerifyNoOutstandingExpectation(); + [Fact] + public async Task when_a_DeleteTransaction_with_notification_is_called_then_we_should_have_called_the_transaction_delete_once() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Delete, "http://localhost/api/transaction/transaction Id") + .WithHeaders("Content-Type", "application/json") + //.With(matcher => matcher.Content.ToString().Contains("'SendNotifications': true")) + .Respond(HttpStatusCode.OK, new StringContent(JsonResources.DeleteTransaction)); + + using (var httpClient = mockHttp.ToHttpClient()) { + var signhostApiClient = new SignhostApiClient(settings, httpClient); + + await signhostApiClient.DeleteTransactionAsync( + "transaction Id", + new DeleteTransactionOptions { SendNotifications = true }); } - [Fact] - public async Task when_AddOrReplaceFileToTransaction_is_called_then_we_should_have_called_the_file_put_once() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Put, "http://localhost/api/transaction/transaction Id/file/file Id") - .WithHeaders("Content-Type", "application/pdf") - .WithHeaders("Digest", "SHA-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=") - .Respond(HttpStatusCode.OK); - - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); - - // Create a 0 sized file - using (Stream file = System.IO.File.Create("unittestdocument.pdf")) { - await signhostApiClient.AddOrReplaceFileToTransaction(file, "transaction Id", "file Id"); - } - } + mockHttp.VerifyNoOutstandingExpectation(); + } - mockHttp.VerifyNoOutstandingExpectation(); + [Fact] + public async Task when_AddOrReplaceFileToTransaction_is_called_then_we_should_have_called_the_file_put_once() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Put, "http://localhost/api/transaction/transaction Id/file/file Id") + .WithHeaders("Content-Type", "application/pdf") + .WithHeaders("Digest", "SHA-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=") + .Respond(HttpStatusCode.OK); + + using (var httpClient = mockHttp.ToHttpClient()) { + var signhostApiClient = new SignhostApiClient(settings, httpClient); + + // Create a 0 sized file + using (Stream file = System.IO.File.Create("unittestdocument.pdf")) { + await signhostApiClient.AddOrReplaceFileToTransaction(file, "transaction Id", "file Id"); + } } - [Fact] - public async Task when_AddOrReplaceFileToTransaction_is_called_default_digest_is_sha256() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Put, "http://localhost/api/transaction/transaction Id/file/file Id") - .WithHeaders("Digest", "SHA-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=") - .Respond(HttpStatusCode.OK); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + [Fact] + public async Task when_AddOrReplaceFileToTransaction_is_called_default_digest_is_sha256() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Put, "http://localhost/api/transaction/transaction Id/file/file Id") + .WithHeaders("Digest", "SHA-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=") + .Respond(HttpStatusCode.OK); - await signhostApiClient.AddOrReplaceFileToTransaction(new MemoryStream(), "transaction Id", "file Id"); - } + using (var httpClient = mockHttp.ToHttpClient()) { + var signhostApiClient = new SignhostApiClient(settings, httpClient); - mockHttp.VerifyNoOutstandingExpectation(); + await signhostApiClient.AddOrReplaceFileToTransaction(new MemoryStream(), "transaction Id", "file Id"); } - [Fact] - public async Task when_AddOrReplaceFileToTransaction_with_sha512_is_called_default_digest_is_sha512() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Put, "http://localhost/api/transaction/transaction Id/file/file Id") - .WithHeaders("Digest", "SHA-512=z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==") - .Respond(HttpStatusCode.OK); - - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); - - await signhostApiClient.AddOrReplaceFileToTransactionAsync( - new MemoryStream(), - "transaction Id", - "file Id", - new FileUploadOptions{ - DigestOptions = new FileDigestOptions - { - DigestHashAlgorithm = "SHA-512" - } - }); - } + mockHttp.VerifyNoOutstandingExpectation(); + } - mockHttp.VerifyNoOutstandingExpectation(); + [Fact] + public async Task when_AddOrReplaceFileToTransaction_with_sha512_is_called_default_digest_is_sha512() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Put, "http://localhost/api/transaction/transaction Id/file/file Id") + .WithHeaders("Digest", "SHA-512=z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==") + .Respond(HttpStatusCode.OK); + + using (var httpClient = mockHttp.ToHttpClient()) { + var signhostApiClient = new SignhostApiClient(settings, httpClient); + + await signhostApiClient.AddOrReplaceFileToTransactionAsync( + new MemoryStream(), + "transaction Id", + "file Id", + new FileUploadOptions{ + DigestOptions = new FileDigestOptions + { + DigestHashAlgorithm = "SHA-512" + } + }); } - [Fact] - public async Task when_AddOrReplaceFileToTransaction_with_digest_value_is_used_as_is() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Put, "http://localhost/api/transaction/transaction Id/file/file Id") - .WithHeaders("Digest", "SHA-1=AAEC") - .Respond(HttpStatusCode.OK); - - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); - - await signhostApiClient.AddOrReplaceFileToTransactionAsync( - new MemoryStream(), - "transaction Id", - "file Id", - new FileUploadOptions - { - DigestOptions = new FileDigestOptions - { - DigestHashAlgorithm = "SHA-1", - DigestHashValue = new byte[] { 0x00, 0x01, 0x02 } - } - }); - } + mockHttp.VerifyNoOutstandingExpectation(); + } - mockHttp.VerifyNoOutstandingExpectation(); + [Fact] + public async Task when_AddOrReplaceFileToTransaction_with_digest_value_is_used_as_is() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Put, "http://localhost/api/transaction/transaction Id/file/file Id") + .WithHeaders("Digest", "SHA-1=AAEC") + .Respond(HttpStatusCode.OK); + + using (var httpClient = mockHttp.ToHttpClient()) { + var signhostApiClient = new SignhostApiClient(settings, httpClient); + + await signhostApiClient.AddOrReplaceFileToTransactionAsync( + new MemoryStream(), + "transaction Id", + "file Id", + new FileUploadOptions + { + DigestOptions = new FileDigestOptions + { + DigestHashAlgorithm = "SHA-1", + DigestHashValue = new byte[] { 0x00, 0x01, 0x02 } + } + }); } - [Fact] - public async Task when_StartTransaction_is_called_then_we_should_have_called_the_transaction_put_once() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp.Expect("http://localhost/api/transaction/transaction Id/start") - .Respond(HttpStatusCode.NoContent); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { + [Fact] + public async Task when_StartTransaction_is_called_then_we_should_have_called_the_transaction_put_once() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp.Expect("http://localhost/api/transaction/transaction Id/start") + .Respond(HttpStatusCode.NoContent); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using (var httpClient = mockHttp.ToHttpClient()) { - await signhostApiClient.StartTransactionAsync("transaction Id"); - } + var signhostApiClient = new SignhostApiClient(settings, httpClient); - mockHttp.VerifyNoOutstandingExpectation(); + await signhostApiClient.StartTransactionAsync("transaction Id"); } - [Fact] - public async Task when_GetReceipt_is_called_then_we_should_have_called_the_filereceipt_get_once() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp.Expect("http://localhost/api/file/receipt/transaction ID") - .Respond(HttpStatusCode.OK); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + [Fact] + public async Task when_GetReceipt_is_called_then_we_should_have_called_the_filereceipt_get_once() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp.Expect("http://localhost/api/file/receipt/transaction ID") + .Respond(HttpStatusCode.OK); - var receipt = await signhostApiClient.GetReceiptAsync("transaction ID"); - } + using (var httpClient = mockHttp.ToHttpClient()) { + var signhostApiClient = new SignhostApiClient(settings, httpClient); - mockHttp.VerifyNoOutstandingExpectation(); + var receipt = await signhostApiClient.GetReceiptAsync("transaction ID"); } - [Fact] - public async Task when_GetDocument_is_called_then_we_should_have_called_the_file_get_once() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp.Expect(HttpMethod.Get, "http://localhost/api/transaction/*/file/file Id") - .Respond(HttpStatusCode.OK, new StringContent(string.Empty)); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { + [Fact] + public async Task when_GetDocument_is_called_then_we_should_have_called_the_file_get_once() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp.Expect(HttpMethod.Get, "http://localhost/api/transaction/*/file/file Id") + .Respond(HttpStatusCode.OK, new StringContent(string.Empty)); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using (var httpClient = mockHttp.ToHttpClient()) { - var document = await signhostApiClient.GetDocumentAsync("transaction Id", "file Id"); - } + var signhostApiClient = new SignhostApiClient(settings, httpClient); - mockHttp.VerifyNoOutstandingExpectation(); + var document = await signhostApiClient.GetDocumentAsync("transaction Id", "file Id"); } - [Fact] - public async Task When_a_transaction_json_is_returned_it_is_deserialized_correctly() - { - var mockHttp = new MockHttpMessageHandler(); - mockHttp.Expect(HttpMethod.Post, "http://localhost/api/transaction") - .Respond(HttpStatusCode.OK, new StringContent(JsonResources.TransactionSingleSignerJson)); + mockHttp.VerifyNoOutstandingExpectation(); + } - using (var httpClient = mockHttp.ToHttpClient()) { + [Fact] + public async Task When_a_transaction_json_is_returned_it_is_deserialized_correctly() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp.Expect(HttpMethod.Post, "http://localhost/api/transaction") + .Respond(HttpStatusCode.OK, new StringContent(JsonResources.TransactionSingleSignerJson)); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using (var httpClient = mockHttp.ToHttpClient()) { - var result = await signhostApiClient.CreateTransactionAsync(new Transaction - { - Signers = new List{ - new Signer + var signhostApiClient = new SignhostApiClient(settings, httpClient); + + var result = await signhostApiClient.CreateTransactionAsync(new Transaction + { + Signers = new List{ + new Signer + { + Verifications = new List { - Verifications = new List + new PhoneNumberVerification { - new PhoneNumberVerification - { - Number = "31615087075" - } + Number = "31615087075" } } } - }); + } + }); + + result.Id.Should().Be("50262c3f-9744-45bf-a4c6-8a3whatever"); + result.CanceledDateTime.Should().HaveYear(2017); + result.Status.Should().Be(TransactionStatus.WaitingForDocument); + result.Signers.Should().HaveCount(1); + result.Receivers.Should().HaveCount(0); + result.Reference.Should().Be("Contract #123"); + result.SignRequestMode.Should().Be(2); + result.DaysToExpire.Should().Be(14); + result.Signers[0].Id.Should().Be("Signer1"); + result.Signers[0].Email.Should().Be("test1@example.com"); + result.Signers[0].Verifications.Should().HaveCount(1); + result.Signers[0].Verifications[0].Should().BeOfType() + .And.Subject.Should().BeEquivalentTo(new PhoneNumberVerification { + Number = "+31615123456" + }); + result.Signers[0].Activities.Should().HaveCount(3); + result.Signers[0].Activities[0].Should().BeEquivalentTo(new Activity + { + Id = "Activity1", + Code = ActivityType.Opened, + CreatedDateTime = DateTimeOffset.Parse("2017-05-31T22:15:17.6409005+02:00") + }); + } - result.Id.Should().Be("50262c3f-9744-45bf-a4c6-8a3whatever"); - result.CanceledDateTime.Should().HaveYear(2017); - result.Status.Should().Be(TransactionStatus.WaitingForDocument); - result.Signers.Should().HaveCount(1); - result.Receivers.Should().HaveCount(0); - result.Reference.Should().Be("Contract #123"); - result.SignRequestMode.Should().Be(2); - result.DaysToExpire.Should().Be(14); - result.Signers[0].Id.Should().Be("Signer1"); - result.Signers[0].Email.Should().Be("test1@example.com"); - result.Signers[0].Verifications.Should().HaveCount(1); - result.Signers[0].Verifications[0].Should().BeOfType() - .And.Subject.Should().BeEquivalentTo(new PhoneNumberVerification { - Number = "+31615123456" - }); - result.Signers[0].Activities.Should().HaveCount(3); - result.Signers[0].Activities[0].Should().BeEquivalentTo(new Activity - { - Id = "Activity1", - Code = ActivityType.Opened, - CreatedDateTime = DateTimeOffset.Parse("2017-05-31T22:15:17.6409005+02:00") - }); + mockHttp.VerifyNoOutstandingExpectation(); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task When_a_complete_transaction_flow_is_created_headers_are_not_set_multiple_times( + bool isOauth) + { + MockedRequest AddHeaders(MockedRequest request) + { + if (!isOauth) { + request = request.WithHeaders("Authorization", "APIKey Usertoken"); } - mockHttp.VerifyNoOutstandingExpectation(); + return request + .WithHeaders("Application", "APPKey AppKey") + .WithHeaders("X-Custom", "test"); } - [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task When_a_complete_transaction_flow_is_created_headers_are_not_set_multiple_times( - bool isOauth) - { - MockedRequest AddHeaders(MockedRequest request) - { - if (!isOauth) { - request = request.WithHeaders("Authorization", "APIKey Usertoken"); - } - - return request - .WithHeaders("Application", "APPKey AppKey") - .WithHeaders("X-Custom", "test"); + var mockHttp = new MockHttpMessageHandler(); + AddHeaders(mockHttp.Expect(HttpMethod.Post, "http://localhost/api/transaction")) + .Respond(new StringContent(JsonResources.TransactionSingleSignerJson)); + AddHeaders(mockHttp.Expect(HttpMethod.Put, "http://localhost/api/transaction/*/file/somefileid")) + .Respond(HttpStatusCode.Accepted, new StringContent(JsonResources.AddOrReplaceFileMetaToTransaction)); + AddHeaders(mockHttp.Expect(HttpMethod.Put, "http://localhost/api/transaction/*/file/somefileid")) + .Respond(HttpStatusCode.Created); + AddHeaders(mockHttp.Expect(HttpMethod.Put, "http://localhost/api/transaction/*/start")) + .Respond(HttpStatusCode.NoContent); + + using (var httpClient = mockHttp.ToHttpClient()) { + var clientSettings = isOauth ? oauthSettings : settings; + clientSettings.AddHeader = add => add("X-Custom", "test"); + var signhostApiClient = new SignhostApiClient(clientSettings, httpClient); + + var result = await signhostApiClient.CreateTransactionAsync(new Transaction()); + await signhostApiClient.AddOrReplaceFileMetaToTransactionAsync(new FileMeta(), result.Id, "somefileid"); + using (Stream file = System.IO.File.Create("unittestdocument.pdf")) { + await signhostApiClient.AddOrReplaceFileToTransaction(file, result.Id, "somefileid"); } + await signhostApiClient.StartTransactionAsync(result.Id); + } - var mockHttp = new MockHttpMessageHandler(); - AddHeaders(mockHttp.Expect(HttpMethod.Post, "http://localhost/api/transaction")) - .Respond(new StringContent(JsonResources.TransactionSingleSignerJson)); - AddHeaders(mockHttp.Expect(HttpMethod.Put, "http://localhost/api/transaction/*/file/somefileid")) - .Respond(HttpStatusCode.Accepted, new StringContent(JsonResources.AddOrReplaceFileMetaToTransaction)); - AddHeaders(mockHttp.Expect(HttpMethod.Put, "http://localhost/api/transaction/*/file/somefileid")) - .Respond(HttpStatusCode.Created); - AddHeaders(mockHttp.Expect(HttpMethod.Put, "http://localhost/api/transaction/*/start")) - .Respond(HttpStatusCode.NoContent); - - using (var httpClient = mockHttp.ToHttpClient()) { - var clientSettings = isOauth ? oauthSettings : settings; - clientSettings.AddHeader = add => add("X-Custom", "test"); - var signhostApiClient = new SignhostApiClient(clientSettings, httpClient); - - var result = await signhostApiClient.CreateTransactionAsync(new Transaction()); - await signhostApiClient.AddOrReplaceFileMetaToTransactionAsync(new FileMeta(), result.Id, "somefileid"); - using (Stream file = System.IO.File.Create("unittestdocument.pdf")) { - await signhostApiClient.AddOrReplaceFileToTransaction(file, result.Id, "somefileid"); - } - await signhostApiClient.StartTransactionAsync(result.Id); - } + mockHttp.VerifyNoOutstandingExpectation(); + mockHttp.VerifyNoOutstandingRequest(); + } - mockHttp.VerifyNoOutstandingExpectation(); - mockHttp.VerifyNoOutstandingRequest(); - } + [Fact] + public async Task When_a_minimal_response_is_retrieved_list_and_dictionaries_are_not_null() + { + var mockHttp = new MockHttpMessageHandler(); + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/c487be92-0255-40c7-bd7d-20805a65e7d9") + .Respond(new StringContent(JsonResources.MinimalTransactionResponse)); - [Fact] - public async Task When_a_minimal_response_is_retrieved_list_and_dictionaries_are_not_null() + using (var httpClient = mockHttp.ToHttpClient()) { - var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/c487be92-0255-40c7-bd7d-20805a65e7d9") - .Respond(new StringContent(JsonResources.MinimalTransactionResponse)); + var signhostApiClient = new SignhostApiClient(settings, httpClient); - using (var httpClient = mockHttp.ToHttpClient()) - { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + var result = await signhostApiClient.GetTransactionAsync("c487be92-0255-40c7-bd7d-20805a65e7d9"); - var result = await signhostApiClient.GetTransactionAsync("c487be92-0255-40c7-bd7d-20805a65e7d9"); - - result.Signers.Should().BeEmpty(); - result.Receivers.Should().BeEmpty(); - result.Files.Should().BeEmpty(); - } + result.Signers.Should().BeEmpty(); + result.Receivers.Should().BeEmpty(); + result.Files.Should().BeEmpty(); } } } diff --git a/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs b/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs index 198f95bf..4bab87eb 100644 --- a/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs +++ b/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs @@ -4,76 +4,75 @@ using System.Collections.Generic; using SignhostAPIClient.Tests.JSON; -namespace Signhost.APIClient.Rest.Tests +namespace Signhost.APIClient.Rest.Tests; + +public class SignhostApiReceiverTests { - public class SignhostApiReceiverTests - { - private SignhostApiReceiverSettings receiverSettings = new SignhostApiReceiverSettings("SharedSecret"); + private SignhostApiReceiverSettings receiverSettings = new SignhostApiReceiverSettings("SharedSecret"); - [Fact] - public void when_IsPostbackChecksumValid_is_called_with_valid_postback_in_body_then_true_is_returned() - { - // Arrange - IDictionary headers = new Dictionary { { "Content-Type", new[] { "application/json" } } }; - string body = JsonResources.MockPostbackValid; + [Fact] + public void when_IsPostbackChecksumValid_is_called_with_valid_postback_in_body_then_true_is_returned() + { + // Arrange + IDictionary headers = new Dictionary { { "Content-Type", new[] { "application/json" } } }; + string body = JsonResources.MockPostbackValid; - // Act - SignhostApiReceiver signhostApiReceiver = new SignhostApiReceiver(receiverSettings); - bool result = signhostApiReceiver.IsPostbackChecksumValid(headers, body, out Transaction transaction); + // Act + SignhostApiReceiver signhostApiReceiver = new SignhostApiReceiver(receiverSettings); + bool result = signhostApiReceiver.IsPostbackChecksumValid(headers, body, out Transaction transaction); - // Assert - result.Should().BeTrue(); - } + // Assert + result.Should().BeTrue(); + } - [Fact] - public void when_IsPostbackChecksumValid_is_called_with_invalid_postback_in_body_then_false_is_returned() - { - // Arrange - IDictionary headers = new Dictionary { { "Content-Type", new[] { "application/json" } } }; - string body = JsonResources.MockPostbackInvalid; + [Fact] + public void when_IsPostbackChecksumValid_is_called_with_invalid_postback_in_body_then_false_is_returned() + { + // Arrange + IDictionary headers = new Dictionary { { "Content-Type", new[] { "application/json" } } }; + string body = JsonResources.MockPostbackInvalid; - // Act - SignhostApiReceiver signhostApiReceiver = new SignhostApiReceiver(receiverSettings); - bool result = signhostApiReceiver.IsPostbackChecksumValid(headers, body, out Transaction transaction); + // Act + SignhostApiReceiver signhostApiReceiver = new SignhostApiReceiver(receiverSettings); + bool result = signhostApiReceiver.IsPostbackChecksumValid(headers, body, out Transaction transaction); - // Assert - result.Should().BeFalse(); - } + // Assert + result.Should().BeFalse(); + } - [Fact] - public void when_IsPostbackChecksumValid_is_called_with_valid_postback_in_header_then_true_is_returned() - { - // Arrange - IDictionary headers = new Dictionary { - { "Content-Type", new[] { "application/json" }}, - {"Checksum", new[] {"cdc09eee2ed6df2846dcc193aedfef59f2834f8d"}} - }; - string body = JsonResources.MockPostbackValid; + [Fact] + public void when_IsPostbackChecksumValid_is_called_with_valid_postback_in_header_then_true_is_returned() + { + // Arrange + IDictionary headers = new Dictionary { + { "Content-Type", new[] { "application/json" }}, + {"Checksum", new[] {"cdc09eee2ed6df2846dcc193aedfef59f2834f8d"}} + }; + string body = JsonResources.MockPostbackValid; - // Act - SignhostApiReceiver signhostApiReceiver = new SignhostApiReceiver(receiverSettings); - bool result = signhostApiReceiver.IsPostbackChecksumValid(headers, body, out Transaction transaction); + // Act + SignhostApiReceiver signhostApiReceiver = new SignhostApiReceiver(receiverSettings); + bool result = signhostApiReceiver.IsPostbackChecksumValid(headers, body, out Transaction transaction); - // Assert - result.Should().BeTrue(); - } + // Assert + result.Should().BeTrue(); + } - [Fact] - public void when_IsPostbackChecksumValid_is_called_with_invalid_postback_in_header_then_false_is_returned() - { - // Arrange - IDictionary headers = new Dictionary { - { "Content-Type", new[] { "application/json" }}, - {"Checksum", new[] {"70dda90616f744797972c0d2f787f86643a60c83"}} - }; - string body = JsonResources.MockPostbackValid; + [Fact] + public void when_IsPostbackChecksumValid_is_called_with_invalid_postback_in_header_then_false_is_returned() + { + // Arrange + IDictionary headers = new Dictionary { + { "Content-Type", new[] { "application/json" }}, + {"Checksum", new[] {"70dda90616f744797972c0d2f787f86643a60c83"}} + }; + string body = JsonResources.MockPostbackValid; - // Act - SignhostApiReceiver signhostApiReceiver = new SignhostApiReceiver(receiverSettings); - bool result = signhostApiReceiver.IsPostbackChecksumValid(headers, body, out Transaction transaction); + // Act + SignhostApiReceiver signhostApiReceiver = new SignhostApiReceiver(receiverSettings); + bool result = signhostApiReceiver.IsPostbackChecksumValid(headers, body, out Transaction transaction); - // Assert - result.Should().BeFalse(); - } + // Assert + result.Should().BeFalse(); } } diff --git a/src/SignhostAPIClient/Rest/ApiResponse.cs b/src/SignhostAPIClient/Rest/ApiResponse.cs index 42e21340..bdd84e37 100644 --- a/src/SignhostAPIClient/Rest/ApiResponse.cs +++ b/src/SignhostAPIClient/Rest/ApiResponse.cs @@ -1,36 +1,35 @@ using System.Net; using System.Net.Http; -namespace Signhost.APIClient.Rest +namespace Signhost.APIClient.Rest; + +public class ApiResponse { - public class ApiResponse - { - private readonly HttpResponseMessage httpResponse; + private readonly HttpResponseMessage httpResponse; - public ApiResponse(HttpResponseMessage httpResponse, TValue value) - { - this.httpResponse = httpResponse; - this.Value = value; - } + public ApiResponse(HttpResponseMessage httpResponse, TValue value) + { + this.httpResponse = httpResponse; + this.Value = value; + } - public TValue Value { get; private set; } + public TValue Value { get; private set; } - public HttpStatusCode HttpStatusCode => httpResponse.StatusCode; + public HttpStatusCode HttpStatusCode => httpResponse.StatusCode; - public void EnsureAvailableStatusCode() - { - if (HttpStatusCode == HttpStatusCode.Gone) { - throw new ErrorHandling.GoneException( - httpResponse.ReasonPhrase, - Value) - { - // TO-DO: Make async in v5 - ResponseBody = httpResponse.Content.ReadAsStringAsync() - .ConfigureAwait(false) - .GetAwaiter() - .GetResult(), - }; - } + public void EnsureAvailableStatusCode() + { + if (HttpStatusCode == HttpStatusCode.Gone) { + throw new ErrorHandling.GoneException( + httpResponse.ReasonPhrase, + Value) + { + // TO-DO: Make async in v5 + ResponseBody = httpResponse.Content.ReadAsStringAsync() + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(), + }; } } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Activity.cs b/src/SignhostAPIClient/Rest/DataObjects/Activity.cs index 517d255b..37a9811d 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Activity.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Activity.cs @@ -1,19 +1,18 @@ using System; using System.Text.Json.Serialization; -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class Activity { - public class Activity - { - public string Id { get; set; } + public string Id { get; set; } - public ActivityType Code { get; set; } + public ActivityType Code { get; set; } - [JsonPropertyName("Activity")] - public string ActivityValue { get; set; } + [JsonPropertyName("Activity")] + public string ActivityValue { get; set; } - public string Info { get; set; } + public string Info { get; set; } - public DateTimeOffset CreatedDateTime { get; set; } - } + public DateTimeOffset CreatedDateTime { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs b/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs index 576f9eb1..223be522 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs @@ -1,136 +1,135 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +/// +/// type. +/// +// TO-DO: Remove unused activity types in v5. +public enum ActivityType { /// - /// type. - /// - // TO-DO: Remove unused activity types in v5. - public enum ActivityType - { - /// - /// The invitation mail was sent. - /// - InvitationSent = 101, - - /// - /// The invitation mail was received. - /// - InvitationReceived = 102, - - /// - /// The sign url was opened. - /// - Opened = 103, - - /// - /// An invitation reminder mail was sent. - /// - InvitationReminderResent = 104, - - /// - /// The document was opened. - /// The contains the fileId of the opened - /// document. - /// - DocumentOpened = 105, - - /// - /// Consumer Signing identity approved. - /// - IdentityApproved = 110, - - /// - /// Consumer Signing identity failed. - /// - IdentityFailed = 111, - - /// - /// Cancelled. - /// - Cancelled = 201, - - /// - /// The signer rejected the sign request. - /// - Rejected = 202, - - /// - /// The signer signed the documents. - /// - Signed = 203, - - /// - /// The signer delegated signing to a different signer. - /// - SignerDelegated = 204, - - /// - /// Signed document sent. - /// - SignedDocumentSent = 301, - - /// - /// Signed document opened. - /// - SignedDocumentOpened = 302, - - /// - /// Signed document downloaded. - /// - SignedDocumentDownloaded = 303, - - /// - /// Receipt sent. - /// - ReceiptSent = 401, - - /// - /// Receipt opened. - /// - ReceiptOpened = 402, - - /// - /// Receipt downloaded. - /// - ReceiptDownloaded = 403, - - /// - /// Finished. - /// - Finished = 500, - - /// - /// Deleted. - /// - Deleted = 600, - - /// - /// Expired. - /// - Expired = 700, - - /// - /// Email bounce - hard. - /// - EmailBounceHard = 901, - - /// - /// Email bounce - soft. - /// - EmailBounceSoft = 902, - - /// - /// Email bounce - blocked. - /// - EmailBounceBlocked = 903, - - /// - /// Email bounce - undetermined. - /// - EmailBounceUndetermined = 904, - - /// - /// Operation failed. - /// - Failed = 999, - } + /// The invitation mail was sent. + /// + InvitationSent = 101, + + /// + /// The invitation mail was received. + /// + InvitationReceived = 102, + + /// + /// The sign url was opened. + /// + Opened = 103, + + /// + /// An invitation reminder mail was sent. + /// + InvitationReminderResent = 104, + + /// + /// The document was opened. + /// The contains the fileId of the opened + /// document. + /// + DocumentOpened = 105, + + /// + /// Consumer Signing identity approved. + /// + IdentityApproved = 110, + + /// + /// Consumer Signing identity failed. + /// + IdentityFailed = 111, + + /// + /// Cancelled. + /// + Cancelled = 201, + + /// + /// The signer rejected the sign request. + /// + Rejected = 202, + + /// + /// The signer signed the documents. + /// + Signed = 203, + + /// + /// The signer delegated signing to a different signer. + /// + SignerDelegated = 204, + + /// + /// Signed document sent. + /// + SignedDocumentSent = 301, + + /// + /// Signed document opened. + /// + SignedDocumentOpened = 302, + + /// + /// Signed document downloaded. + /// + SignedDocumentDownloaded = 303, + + /// + /// Receipt sent. + /// + ReceiptSent = 401, + + /// + /// Receipt opened. + /// + ReceiptOpened = 402, + + /// + /// Receipt downloaded. + /// + ReceiptDownloaded = 403, + + /// + /// Finished. + /// + Finished = 500, + + /// + /// Deleted. + /// + Deleted = 600, + + /// + /// Expired. + /// + Expired = 700, + + /// + /// Email bounce - hard. + /// + EmailBounceHard = 901, + + /// + /// Email bounce - soft. + /// + EmailBounceSoft = 902, + + /// + /// Email bounce - blocked. + /// + EmailBounceBlocked = 903, + + /// + /// Email bounce - undetermined. + /// + EmailBounceUndetermined = 904, + + /// + /// Operation failed. + /// + Failed = 999, } diff --git a/src/SignhostAPIClient/Rest/DataObjects/ConsentVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/ConsentVerification.cs index 00289618..6a045ce3 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ConsentVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/ConsentVerification.cs @@ -1,10 +1,9 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +/// +/// Adds a consent verification screen +/// +public class ConsentVerification + : IVerification { - /// - /// Adds a consent verification screen - /// - public class ConsentVerification - : IVerification - { - } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/CscVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/CscVerification.cs index 2ceb0de4..3befa8de 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/CscVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/CscVerification.cs @@ -1,36 +1,35 @@ using System.Collections.Generic; -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +/// +/// Cloud Signature Consortium (CSC) verification. +/// +public class CscVerification + : IVerification { /// - /// Cloud Signature Consortium (CSC) verification. + /// Gets or sets the provider identifier. /// - public class CscVerification - : IVerification - { - /// - /// Gets or sets the provider identifier. - /// - public string Provider { get; set; } + public string Provider { get; set; } - /// - /// Gets or sets the certificate issuer. - /// - public string Issuer { get; set; } + /// + /// Gets or sets the certificate issuer. + /// + public string Issuer { get; set; } - /// - /// Gets or sets the certificate subject. - /// - public string Subject { get; set; } + /// + /// Gets or sets the certificate subject. + /// + public string Subject { get; set; } - /// - /// Gets or sets the certificate thumbprint. - /// - public string Thumbprint { get; set; } + /// + /// Gets or sets the certificate thumbprint. + /// + public string Thumbprint { get; set; } - /// - /// Gets or sets additional user data. - /// - public Dictionary AdditionalUserData { get; set; } - } + /// + /// Gets or sets additional user data. + /// + public Dictionary AdditionalUserData { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/DeleteTransactionOptions.cs b/src/SignhostAPIClient/Rest/DataObjects/DeleteTransactionOptions.cs index 19ad295d..ae3ecc5a 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/DeleteTransactionOptions.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/DeleteTransactionOptions.cs @@ -1,16 +1,15 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class DeleteTransactionOptions { - public class DeleteTransactionOptions - { - /// - /// Gets or sets a value indicating whether - /// e-mail notifications should be send to the awaiting signers. - /// - public bool SendNotifications { get; set; } + /// + /// Gets or sets a value indicating whether + /// e-mail notifications should be send to the awaiting signers. + /// + public bool SendNotifications { get; set; } - /// - /// Gets or sets the reason of cancellation. - /// - public string Reason { get; set; } - } + /// + /// Gets or sets the reason of cancellation. + /// + public string Reason { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/DigidVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/DigidVerification.cs index bfb8b9f2..6ec39b90 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/DigidVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/DigidVerification.cs @@ -1,10 +1,9 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class DigidVerification + : IVerification { - public class DigidVerification - : IVerification - { - public string Bsn { get; set; } + public string Bsn { get; set; } - public bool? SecureDownload { get; set; } - } + public bool? SecureDownload { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/EherkenningVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/EherkenningVerification.cs index 647b1c0e..838144b6 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/EherkenningVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/EherkenningVerification.cs @@ -1,16 +1,15 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class EherkenningVerification + : IVerification { - public class EherkenningVerification - : IVerification - { - /// - /// Gets or sets the Uid. - /// - public string Uid { get; set; } + /// + /// Gets or sets the Uid. + /// + public string Uid { get; set; } - /// - /// Gets or sets the entity concern ID / KVK number. - /// - public string EntityConcernIdKvkNr { get; set; } - } + /// + /// Gets or sets the entity concern ID / KVK number. + /// + public string EntityConcernIdKvkNr { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/EidasLoginVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/EidasLoginVerification.cs index 18ac4afa..3d54b0ef 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/EidasLoginVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/EidasLoginVerification.cs @@ -3,43 +3,42 @@ using System.Text.Json.Serialization; using Signhost.APIClient.Rest.JsonConverters; -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +/// +/// Verification object for eIDAS. +/// +public class EidasLoginVerification + : IVerification { /// - /// Verification object for eIDAS. + /// Gets or sets the uid. /// - public class EidasLoginVerification - : IVerification - { - /// - /// Gets or sets the uid. - /// - public string Uid { get; set; } + public string Uid { get; set; } - /// - /// Gets or sets the level. - /// - [JsonConverter(typeof(LevelEnumConverter))] - public Level? Level { get; set; } + /// + /// Gets or sets the level. + /// + [JsonConverter(typeof(LevelEnumConverter))] + public Level? Level { get; set; } - /// - /// Gets or sets the first name. - /// - public string FirstName { get; set; } + /// + /// Gets or sets the first name. + /// + public string FirstName { get; set; } - /// - /// Gets or sets the last name. - /// - public string LastName { get; set; } + /// + /// Gets or sets the last name. + /// + public string LastName { get; set; } - /// - /// Gets or sets the date of birth. - /// - public DateTime? DateOfBirth { get; set; } + /// + /// Gets or sets the date of birth. + /// + public DateTime? DateOfBirth { get; set; } - /// - /// Gets or sets the eIDAS attributes. - /// - public IDictionary Attributes { get; set; } - } + /// + /// Gets or sets the eIDAS attributes. + /// + public IDictionary Attributes { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Field.cs b/src/SignhostAPIClient/Rest/DataObjects/Field.cs index a7453c8a..58cd3f9d 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Field.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Field.cs @@ -1,13 +1,12 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class Field { - public class Field - { - // TO-DO: Make enum in v5. - public string Type { get; set; } + // TO-DO: Make enum in v5. + public string Type { get; set; } - // TO-DO: Can be boolean, number, string, should be fixed in v5. - public string Value { get; set; } + // TO-DO: Can be boolean, number, string, should be fixed in v5. + public string Value { get; set; } - public Location Location { get; set; } - } + public Location Location { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileEntry.cs b/src/SignhostAPIClient/Rest/DataObjects/FileEntry.cs index 5a3cc1bf..f1565e5e 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileEntry.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileEntry.cs @@ -1,11 +1,10 @@ using System.Collections.Generic; -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class FileEntry { - public class FileEntry - { - public IList Links { get; set; } + public IList Links { get; set; } - public string DisplayName { get; set; } - } + public string DisplayName { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileLink.cs b/src/SignhostAPIClient/Rest/DataObjects/FileLink.cs index d1fb0323..f503d5ae 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileLink.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileLink.cs @@ -1,11 +1,10 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class FileLink { - public class FileLink - { - public string Rel { get; set; } + public string Rel { get; set; } - public string Type { get; set; } + public string Type { get; set; } - public string Link { get; set; } - } + public string Link { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileMeta.cs b/src/SignhostAPIClient/Rest/DataObjects/FileMeta.cs index 6cbda5a0..b20b72e5 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileMeta.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileMeta.cs @@ -1,27 +1,26 @@ using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class FileMeta { - public class FileMeta - { - public int? DisplayOrder { get; set; } + public int? DisplayOrder { get; set; } - public string DisplayName { get; set; } + public string DisplayName { get; set; } - public string Description { get; set; } + public string Description { get; set; } - public IDictionary Signers { get; set; } + public IDictionary Signers { get; set; } - public IDictionary> FormSets { get; set; } + public IDictionary> FormSets { get; set; } - /// - /// Gets or sets whether to use the scribble signature as a paraph - /// on each non-signed page. - /// Don't use this setting unless you are really sure this is what you - /// want and know the side-effects. - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public bool? SetParaph { get; set; } - } + /// + /// Gets or sets whether to use the scribble signature as a paraph + /// on each non-signed page. + /// Don't use this setting unless you are really sure this is what you + /// want and know the side-effects. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? SetParaph { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileSignerMeta.cs b/src/SignhostAPIClient/Rest/DataObjects/FileSignerMeta.cs index 1c39aa83..7adfc404 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileSignerMeta.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileSignerMeta.cs @@ -1,7 +1,6 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class FileSignerMeta { - public class FileSignerMeta - { - public string[] FormSets { get; set; } - } + public string[] FormSets { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs index cb68fcda..b1fb67f3 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs @@ -1,14 +1,13 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +/// +/// Adds a consent verification screen +/// +public class IPAddressVerification + : IVerification { /// - /// Adds a consent verification screen + /// Gets or sets the IP Address used by the signer while signing the documents. /// - public class IPAddressVerification - : IVerification - { - /// - /// Gets or sets the IP Address used by the signer while signing the documents. - /// - public string IPAddress { get; set; } - } + public string IPAddress { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/IVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/IVerification.cs index bbab8eac..de6a2977 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/IVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/IVerification.cs @@ -1,24 +1,22 @@ using System.Text.Json.Serialization; -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +[JsonPolymorphic(TypeDiscriminatorPropertyName = "Type")] +[JsonDerivedType(typeof(ConsentVerification), "Consent")] +[JsonDerivedType(typeof(DigidVerification), "DigiD")] +[JsonDerivedType(typeof(EidasLoginVerification), "eIDAS Login")] +[JsonDerivedType(typeof(IdealVerification), "iDeal")] +[JsonDerivedType(typeof(IdinVerification), "iDIN")] +[JsonDerivedType(typeof(IPAddressVerification), "IPAddress")] +[JsonDerivedType(typeof(ItsmeIdentificationVerification), "itsme Identification")] +[JsonDerivedType(typeof(PhoneNumberVerification), "PhoneNumber")] +[JsonDerivedType(typeof(ScribbleVerification), "Scribble")] +[JsonDerivedType(typeof(SurfnetVerification), "SURFnet")] +[JsonDerivedType(typeof(CscVerification), "CSC Qualified")] +[JsonDerivedType(typeof(EherkenningVerification), "eHerkenning")] +[JsonDerivedType(typeof(OidcVerification), "OpenID Providers")] +[JsonDerivedType(typeof(OnfidoVerification), "Onfido")] +public interface IVerification { - // TO-DO: Split to verification and authentication in v5 - [JsonPolymorphic(TypeDiscriminatorPropertyName = "Type")] - [JsonDerivedType(typeof(ConsentVerification), "Consent")] - [JsonDerivedType(typeof(DigidVerification), "DigiD")] - [JsonDerivedType(typeof(EidasLoginVerification), "eIDAS Login")] - [JsonDerivedType(typeof(IdealVerification), "iDeal")] - [JsonDerivedType(typeof(IdinVerification), "iDIN")] - [JsonDerivedType(typeof(IPAddressVerification), "IPAddress")] - [JsonDerivedType(typeof(ItsmeIdentificationVerification), "itsme Identification")] - [JsonDerivedType(typeof(PhoneNumberVerification), "PhoneNumber")] - [JsonDerivedType(typeof(ScribbleVerification), "Scribble")] - [JsonDerivedType(typeof(SurfnetVerification), "SURFnet")] - [JsonDerivedType(typeof(CscVerification), "CSC Qualified")] - [JsonDerivedType(typeof(EherkenningVerification), "eHerkenning")] - [JsonDerivedType(typeof(OidcVerification), "OpenID Providers")] - [JsonDerivedType(typeof(OnfidoVerification), "Onfido")] - public interface IVerification - { - } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/IdealVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/IdealVerification.cs index 5267f284..55f1bc95 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/IdealVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/IdealVerification.cs @@ -1,12 +1,11 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class IdealVerification + : IVerification { - public class IdealVerification - : IVerification - { - public string Iban { get; set; } + public string Iban { get; set; } - public string AccountHolderName { get; set; } + public string AccountHolderName { get; set; } - public string AccountHolderCity { get; set; } - } + public string AccountHolderCity { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/IdinVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/IdinVerification.cs index 38adcd92..150f398b 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/IdinVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/IdinVerification.cs @@ -1,19 +1,18 @@ using System; using System.Collections.Generic; -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class IdinVerification + : IVerification { - public class IdinVerification - : IVerification - { - public string AccountHolderName { get; set; } + public string AccountHolderName { get; set; } - public string AccountHolderAddress1 { get; set; } + public string AccountHolderAddress1 { get; set; } - public string AccountHolderAddress2 { get; set; } + public string AccountHolderAddress2 { get; set; } - public DateTime AccountHolderDateOfBirth { get; set; } + public DateTime AccountHolderDateOfBirth { get; set; } - public IDictionary Attributes { get; set; } - } + public IDictionary Attributes { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/ItsmeIdentificationVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/ItsmeIdentificationVerification.cs index eeae90b8..914665e4 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ItsmeIdentificationVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/ItsmeIdentificationVerification.cs @@ -1,14 +1,13 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +/// +/// Verification object for itsme Identification. +/// +public class ItsmeIdentificationVerification + : IVerification { /// - /// Verification object for itsme Identification. + /// Gets or sets the phonenumber. /// - public class ItsmeIdentificationVerification - : IVerification - { - /// - /// Gets or sets the phonenumber. - /// - public string PhoneNumber { get; set; } - } + public string PhoneNumber { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Level.cs b/src/SignhostAPIClient/Rest/DataObjects/Level.cs index 93f223b1..a96cd7f5 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Level.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Level.cs @@ -1,28 +1,27 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +/// +/// Level of Assurance. +/// +public enum Level { /// - /// Level of Assurance. + /// Unknown. /// - public enum Level - { - /// - /// Unknown. - /// - Unknown = 0, + Unknown = 0, - /// - /// Low. - /// - Low, + /// + /// Low. + /// + Low, - /// - /// Substantial. - /// - Substantial, + /// + /// Substantial. + /// + Substantial, - /// - /// High. - /// - High, - } + /// + /// High. + /// + High, } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Location.cs b/src/SignhostAPIClient/Rest/DataObjects/Location.cs index d7288d99..1799ad6b 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Location.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Location.cs @@ -1,23 +1,22 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class Location { - public class Location - { - public string Search { get; set; } + public string Search { get; set; } - public int? Occurence { get; set; } + public int? Occurence { get; set; } - public int? Top { get; set; } + public int? Top { get; set; } - public int? Right { get; set; } + public int? Right { get; set; } - public int? Bottom { get; set; } + public int? Bottom { get; set; } - public int? Left { get; set; } + public int? Left { get; set; } - public int? Width { get; set; } + public int? Width { get; set; } - public int? Height { get; set; } + public int? Height { get; set; } - public int? PageNumber { get; set; } - } + public int? PageNumber { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/OidcVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/OidcVerification.cs index 57507de1..608f9dff 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/OidcVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/OidcVerification.cs @@ -1,14 +1,13 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +/// +/// OpenID Connect identification. +/// +public class OidcVerification + : IVerification { /// - /// OpenID Connect identification. + /// Gets or sets the OIDC provider name. /// - public class OidcVerification - : IVerification - { - /// - /// Gets or sets the OIDC provider name. - /// - public string ProviderName { get; set; } - } + public string ProviderName { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/OnfidoVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/OnfidoVerification.cs index ae2a6a3e..c980c25b 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/OnfidoVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/OnfidoVerification.cs @@ -1,32 +1,31 @@ using System; using System.Collections.Generic; -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +/// +/// Onfido identity verification. +/// +public class OnfidoVerification + : IVerification { /// - /// Onfido identity verification. + /// Gets or sets the Onfido workflow identifier. /// - public class OnfidoVerification - : IVerification - { - /// - /// Gets or sets the Onfido workflow identifier. - /// - public Guid? WorkflowId { get; set; } + public Guid? WorkflowId { get; set; } - /// - /// Gets or sets the Onfido workflow run identifier. - /// - public Guid? WorkflowRunId { get; set; } + /// + /// Gets or sets the Onfido workflow run identifier. + /// + public Guid? WorkflowRunId { get; set; } - /// - /// Gets or sets the Onfido API version. - /// - public int? Version { get; set; } + /// + /// Gets or sets the Onfido API version. + /// + public int? Version { get; set; } - /// - /// Gets or sets raw Onfido attributes (availability not guaranteed). - /// - public Dictionary Attributes { get; set; } - } + /// + /// Gets or sets raw Onfido attributes (availability not guaranteed). + /// + public Dictionary? Attributes { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/PhoneNumberVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/PhoneNumberVerification.cs index 6881543d..38640a10 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/PhoneNumberVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/PhoneNumberVerification.cs @@ -1,10 +1,9 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class PhoneNumberVerification + : IVerification { - public class PhoneNumberVerification - : IVerification - { - public string Number { get; set; } + public string Number { get; set; } - public bool? SecureDownload { get; set; } - } + public bool? SecureDownload { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs b/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs index ea57648d..cffc6ebb 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs @@ -1,11 +1,10 @@ using System.Text.Json.Serialization; -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class PostbackTransaction + : Transaction { - public class PostbackTransaction - : Transaction - { - [JsonPropertyName("Checksum")] - public string Checksum { get; set; } - } + [JsonPropertyName("Checksum")] + public string Checksum { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Receiver.cs b/src/SignhostAPIClient/Rest/DataObjects/Receiver.cs index be820504..b044b37f 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Receiver.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Receiver.cs @@ -2,41 +2,40 @@ using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class Receiver { - public class Receiver + public Receiver() { - public Receiver() - { - } + } - [JsonConstructor] - private Receiver(IReadOnlyList activities) - { - Activities = activities; - } + [JsonConstructor] + private Receiver(IReadOnlyList activities) + { + Activities = activities; + } - public string Id { get; set; } + public string Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } - public string Email { get; set; } + public string Email { get; set; } - public string Language { get; set; } + public string Language { get; set; } - public string Subject { get; set; } + public string Subject { get; set; } - public string Message { get; set; } + public string Message { get; set; } - public string Reference { get; set; } + public string Reference { get; set; } - public DateTimeOffset? CreatedDateTime { get; set; } + public DateTimeOffset? CreatedDateTime { get; set; } - public DateTimeOffset? ModifiedDateTime { get; set; } + public DateTimeOffset? ModifiedDateTime { get; set; } - public IReadOnlyList Activities { get; set; } = - new List().AsReadOnly(); + public IReadOnlyList Activities { get; set; } = + new List().AsReadOnly(); - public dynamic Context { get; set; } - } + public dynamic Context { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/ScribbleVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/ScribbleVerification.cs index fadb57af..5556f0b5 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ScribbleVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/ScribbleVerification.cs @@ -1,12 +1,11 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class ScribbleVerification + : IVerification { - public class ScribbleVerification - : IVerification - { - public bool RequireHandsignature { get; set; } + public bool RequireHandsignature { get; set; } - public bool ScribbleNameFixed { get; set; } + public bool ScribbleNameFixed { get; set; } - public string ScribbleName { get; set; } - } + public string ScribbleName { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Signer.cs b/src/SignhostAPIClient/Rest/DataObjects/Signer.cs index 5d61f23e..944c2571 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Signer.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Signer.cs @@ -2,85 +2,84 @@ using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class Signer { - public class Signer + public Signer() { - public Signer() - { - } + } - [JsonConstructor] - private Signer(IReadOnlyList activities) - { - Activities = activities; - } + [JsonConstructor] + private Signer(IReadOnlyList activities) + { + Activities = activities; + } - public string Id { get; set; } + public string Id { get; set; } - public DateTimeOffset? Expires { get; set; } + public DateTimeOffset? Expires { get; set; } - public string Email { get; set; } + public string Email { get; set; } - public string IntroText { get; set; } + public string IntroText { get; set; } - public string SignRequestSubject { get; set; } + public string SignRequestSubject { get; set; } - public string SignRequestMessage { get; set; } + public string SignRequestMessage { get; set; } - public IList Authentications { get; set; } - = new List(); + public IList Authentications { get; set; } + = new List(); - public IList Verifications { get; set; } - = new List(); + public IList Verifications { get; set; } + = new List(); - public bool SendSignRequest { get; set; } + public bool SendSignRequest { get; set; } - public bool? SendSignConfirmation { get; set; } + public bool? SendSignConfirmation { get; set; } - public int? DaysToRemind { get; set; } + public int? DaysToRemind { get; set; } - public string Language { get; set; } + public string Language { get; set; } - public string ScribbleName { get; set; } + public string ScribbleName { get; set; } - public bool ScribbleNameFixed { get; set; } + public bool ScribbleNameFixed { get; set; } - public string Reference { get; set; } + public string Reference { get; set; } - public string ReturnUrl { get; set; } + public string ReturnUrl { get; set; } - public string RejectReason { get; set; } + public string RejectReason { get; set; } - public string SignUrl { get; set; } + public string SignUrl { get; set; } - public bool AllowDelegation { get; set; } + public bool AllowDelegation { get; set; } - public string DelegateSignUrl { get; set; } + public string DelegateSignUrl { get; set; } - public string DelegateReason { get; set; } + public string DelegateReason { get; set; } - public string DelegateSignerEmail { get; set; } + public string DelegateSignerEmail { get; set; } - public string DelegateSignerName { get; set; } + public string DelegateSignerName { get; set; } - public DateTimeOffset? SignedDateTime { get; set; } + public DateTimeOffset? SignedDateTime { get; set; } - public DateTimeOffset? RejectDateTime { get; set; } + public DateTimeOffset? RejectDateTime { get; set; } - public DateTimeOffset? CreatedDateTime { get; set; } + public DateTimeOffset? CreatedDateTime { get; set; } - public DateTimeOffset? SignerDelegationDateTime { get; set; } + public DateTimeOffset? SignerDelegationDateTime { get; set; } - public DateTimeOffset? ModifiedDateTime { get; set; } + public DateTimeOffset? ModifiedDateTime { get; set; } - public string ShowUrl { get; set; } + public string ShowUrl { get; set; } - public string ReceiptUrl { get; set; } + public string ReceiptUrl { get; set; } - public IReadOnlyList Activities { get; private set; } = - new List().AsReadOnly(); + public IReadOnlyList Activities { get; private set; } = + new List().AsReadOnly(); - public dynamic Context { get; set; } - } + public dynamic Context { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/SurfnetVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/SurfnetVerification.cs index dfef6fdb..8523e733 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/SurfnetVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/SurfnetVerification.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class SurfnetVerification + : IVerification { - public class SurfnetVerification - : IVerification - { - public string Uid { get; set; } + public string Uid { get; set; } - public IDictionary Attributes { get; set; } - } + public IDictionary Attributes { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs b/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs index 66166334..7ac62205 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs @@ -1,53 +1,52 @@ using System; using System.Collections.Generic; -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class Transaction { - public class Transaction - { - public string Id { get; set; } + public string Id { get; set; } - /// - /// Gets the when the was created. - /// - public DateTimeOffset? CreatedDateTime { get; set; } + /// + /// Gets the when the was created. + /// + public DateTimeOffset? CreatedDateTime { get; set; } - /// - /// Gets the when the was cancelled. - /// Returns null if the transaction was not cancelled. - /// - public DateTimeOffset? CanceledDateTime { get; set; } + /// + /// Gets the when the was cancelled. + /// Returns null if the transaction was not cancelled. + /// + public DateTimeOffset? CanceledDateTime { get; set; } - /// - /// Gets the cancellation reason when the was cancelled. - /// - public string CancellationReason { get; set; } + /// + /// Gets the cancellation reason when the was cancelled. + /// + public string CancellationReason { get; set; } - public IReadOnlyDictionary Files { get; set; } = - new Dictionary(); + public IReadOnlyDictionary Files { get; set; } = + new Dictionary(); - public TransactionStatus Status { get; set; } + public TransactionStatus Status { get; set; } - public bool Seal { get; set; } + public bool Seal { get; set; } - public IList Signers { get; set; } - = new List(); + public IList Signers { get; set; } + = new List(); - public IList Receivers { get; set; } - = new List(); + public IList Receivers { get; set; } + = new List(); - public string Reference { get; set; } + public string Reference { get; set; } - public string PostbackUrl { get; set; } + public string PostbackUrl { get; set; } - public int SignRequestMode { get; set; } + public int SignRequestMode { get; set; } - public int DaysToExpire { get; set; } + public int DaysToExpire { get; set; } - public string Language { get; set; } + public string Language { get; set; } - public bool SendEmailNotifications { get; set; } + public bool SendEmailNotifications { get; set; } - public dynamic Context { get; set; } - } + public dynamic Context { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/TransactionStatus.cs b/src/SignhostAPIClient/Rest/DataObjects/TransactionStatus.cs index 2c9b80a9..7485bf10 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/TransactionStatus.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/TransactionStatus.cs @@ -1,47 +1,46 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public enum TransactionStatus { - public enum TransactionStatus - { - /// - /// Transaction has not yet been started and is waiting for its - /// documents. - /// - WaitingForDocument = 5, - - /// - /// The transaction was started and is waiting for one or more signers - /// to sign the transaction. - /// - WaitingForSigner = 10, - - /// - /// In progress - /// - InProgress = 20, - - /// - /// The transaction was succesfully completed. - /// - Signed = 30, - - /// - /// The transaction was rejected by one or more of the signers. - /// - Rejected = 40, - - /// - /// The transaction was not signed before it expired. - /// - Expired = 50, - - /// - /// The transaction was cancelled by the sender. - /// - Cancelled = 60, - - /// - /// The transaction could not be completed. - /// - Failed = 70, - } + /// + /// Transaction has not yet been started and is waiting for its + /// documents. + /// + WaitingForDocument = 5, + + /// + /// The transaction was started and is waiting for one or more signers + /// to sign the transaction. + /// + WaitingForSigner = 10, + + /// + /// In progress + /// + InProgress = 20, + + /// + /// The transaction was succesfully completed. + /// + Signed = 30, + + /// + /// The transaction was rejected by one or more of the signers. + /// + Rejected = 40, + + /// + /// The transaction was not signed before it expired. + /// + Expired = 50, + + /// + /// The transaction was cancelled by the sender. + /// + Cancelled = 60, + + /// + /// The transaction could not be completed. + /// + Failed = 70, } diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs index 63dde05c..d9d8db32 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs @@ -1,37 +1,36 @@ using System; -namespace Signhost.APIClient.Rest.ErrorHandling +namespace Signhost.APIClient.Rest.ErrorHandling; + +// TO-DO: Use this instead of Unauthorized exception in v5 +[Serializable] +public class BadAuthorizationException + : SignhostRestApiClientException { - // TO-DO: Use this instead of Unauthorized exception in v5 - [Serializable] - public class BadAuthorizationException - : SignhostRestApiClientException + public BadAuthorizationException() + : base("API call returned a 401 error code. Please check your request headers.") { - public BadAuthorizationException() - : base("API call returned a 401 error code. Please check your request headers.") - { - HelpLink = "https://api.signhost.com/Help"; - } + HelpLink = "https://api.signhost.com/Help"; + } - public BadAuthorizationException(string message) - : base(message) - { - } + public BadAuthorizationException(string message) + : base(message) + { + } - public BadAuthorizationException( - string message, - Exception innerException) - : base(message, innerException) - { - } + public BadAuthorizationException( + string message, + Exception innerException) + : base(message, innerException) + { + } #if SERIALIZABLE - protected BadAuthorizationException( - SerializationInfo info, - StreamingContext context) - : base(info, context) - { - } -#endif + protected BadAuthorizationException( + SerializationInfo info, + StreamingContext context) + : base(info, context) + { } +#endif } diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/BadRequestException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/BadRequestException.cs index d6041abf..d449b02b 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/BadRequestException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/BadRequestException.cs @@ -1,36 +1,35 @@ using System; -namespace Signhost.APIClient.Rest.ErrorHandling +namespace Signhost.APIClient.Rest.ErrorHandling; + +[Serializable] +public class BadRequestException + : SignhostRestApiClientException { - [Serializable] - public class BadRequestException - : SignhostRestApiClientException + public BadRequestException() + : base() { - public BadRequestException() - : base() - { - } + } - public BadRequestException(string message) - : base(message) - { - } + public BadRequestException(string message) + : base(message) + { + } - public BadRequestException( - string message, - Exception innerException) - : base(message, innerException) - { - HelpLink = "https://api.signhost.com/Help"; - } + public BadRequestException( + string message, + Exception innerException) + : base(message, innerException) + { + HelpLink = "https://api.signhost.com/Help"; + } #if SERIALIZABLE - protected BadRequestException( - SerializationInfo info, - StreamingContext context) - : base(info, context) - { - } -#endif + protected BadRequestException( + SerializationInfo info, + StreamingContext context) + : base(info, context) + { } +#endif } diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/DefaultSignhostException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/DefaultSignhostException.cs index 8c9a0542..b7db21ab 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/DefaultSignhostException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/DefaultSignhostException.cs @@ -1,30 +1,29 @@ using System; -namespace Signhost.APIClient.Rest.ErrorHandling +namespace Signhost.APIClient.Rest.ErrorHandling; + +[Serializable] +public class DefaultSignhostException : Exception { - [Serializable] - public class DefaultSignhostException : Exception + public DefaultSignhostException(string message) + : base(message) { - public DefaultSignhostException(string message) - : base(message) - { - HelpLink = "https://api.signhost.com/Help"; - } + HelpLink = "https://api.signhost.com/Help"; + } - public DefaultSignhostException( - string message, - Exception innerException) - : base(message, innerException) - { - } + public DefaultSignhostException( + string message, + Exception innerException) + : base(message, innerException) + { + } #if SERIALIZABLE - protected DefaultSignhostException( - SerializationInfo info, - StreamingContext context) - : base(info, context) - { - } -#endif + protected DefaultSignhostException( + SerializationInfo info, + StreamingContext context) + : base(info, context) + { } +#endif } diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/GoneException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/GoneException.cs index 08e8ce8c..5aad3757 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/GoneException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/GoneException.cs @@ -1,62 +1,61 @@ using System; -namespace Signhost.APIClient.Rest.ErrorHandling +namespace Signhost.APIClient.Rest.ErrorHandling; + +/// +/// Thrown when a transaction is deleted / cancelled. +/// +[Serializable] +public class GoneException + : SignhostRestApiClientException { /// - /// Thrown when a transaction is deleted / cancelled. + /// Initializes a new instance of the class. /// - [Serializable] - public class GoneException - : SignhostRestApiClientException + public GoneException() + : base() { - /// - /// Initializes a new instance of the class. - /// - public GoneException() - : base() - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// Additional information - public GoneException(string message) - : base(message) - { - } + /// + /// Initializes a new instance of the class. + /// + /// Additional information + public GoneException(string message) + : base(message) + { + } - public GoneException(string message, TResult result) - : base(message) - { - Result = result; - } + public GoneException(string message, TResult result) + : base(message) + { + Result = result; + } - /// - /// Initializes a new instance of the class. - /// - /// Additional information - /// Inner exception - public GoneException(string message, Exception innerException) - : base(message, innerException) - { - HelpLink = "https://api.signhost.com/Help"; - } + /// + /// Initializes a new instance of the class. + /// + /// Additional information + /// Inner exception + public GoneException(string message, Exception innerException) + : base(message, innerException) + { + HelpLink = "https://api.signhost.com/Help"; + } #if SERIALIZABLE - protected GoneException( - SerializationInfo info, - StreamingContext context) - : base(info, context) - { - } + protected GoneException( + SerializationInfo info, + StreamingContext context) + : base(info, context) + { + } #endif - /// - /// Gets the api / transaction details which are still available. - /// Please note that this no longer contains the full transaction - /// details as most data is removed. - /// - public TResult Result { get; private set; } - } + /// + /// Gets the api / transaction details which are still available. + /// Please note that this no longer contains the full transaction + /// details as most data is removed. + /// + public TResult Result { get; private set; } } diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs b/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs index 55e09ec3..21e6b51d 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs @@ -6,120 +6,119 @@ using System.Text.Json.Serialization; using System.Threading.Tasks; -namespace Signhost.APIClient.Rest.ErrorHandling +namespace Signhost.APIClient.Rest.ErrorHandling; + +/// +/// Error handling around s. +/// +public static class HttpResponseMessageErrorHandlingExtensions { + private const string OutOfCreditsApiProblemType = + "https://api.signhost.com/problem/subscription/out-of-credits"; + /// - /// Error handling around s. + /// Throws an exception if the + /// has an error code. /// - public static class HttpResponseMessageErrorHandlingExtensions + /// + /// Returns if the call is succesful. + /// List of which should + /// not be handled as an error. + /// + /// When the api authentication failed. + /// + /// + /// When the API request was an invalid request for your account. + /// + /// + /// When your organisation has run out of credits. + /// + /// + /// When the request resource (ie transaction id or file id) was not found. + /// + /// + /// When the API was unable to proces the request at the moment, + /// a RetryAfter property is set if available. + /// + /// + /// An other unknown API error occured. + /// + public static async Task EnsureSignhostSuccessStatusCodeAsync( + this Task responseTask, + params HttpStatusCode[] expectedStatusCodes) { - private const string OutOfCreditsApiProblemType = - "https://api.signhost.com/problem/subscription/out-of-credits"; - - /// - /// Throws an exception if the - /// has an error code. - /// - /// - /// Returns if the call is succesful. - /// List of which should - /// not be handled as an error. - /// - /// When the api authentication failed. - /// - /// - /// When the API request was an invalid request for your account. - /// - /// - /// When your organisation has run out of credits. - /// - /// - /// When the request resource (ie transaction id or file id) was not found. - /// - /// - /// When the API was unable to proces the request at the moment, - /// a RetryAfter property is set if available. - /// - /// - /// An other unknown API error occured. - /// - public static async Task EnsureSignhostSuccessStatusCodeAsync( - this Task responseTask, - params HttpStatusCode[] expectedStatusCodes) - { - var response = await responseTask.ConfigureAwait(false); - - if (response.IsSuccessStatusCode) { - return response; - } - - if (expectedStatusCodes.Contains(response.StatusCode)) { - return response; - } - - string errorType = string.Empty; - string errorMessage = "Unknown Signhost error"; - string responseBody = string.Empty; - - if (response.Content != null) { - responseBody = await response.Content.ReadAsStringAsync() - .ConfigureAwait(false); - - var error = JsonSerializer.Deserialize(responseBody); - - errorType = error?.Type ?? string.Empty; - errorMessage = error?.Message ?? "Unknown Signhost error"; - } - - // TO-DO: Use switch pattern in v5 - Exception exception = null; - switch (response.StatusCode) { - case HttpStatusCode.Unauthorized: - exception = new UnauthorizedAccessException( - errorMessage); - break; - - case HttpStatusCode.BadRequest: - exception = new BadRequestException( - errorMessage); - break; - - case HttpStatusCode.PaymentRequired - when errorType == OutOfCreditsApiProblemType: - exception = new OutOfCreditsException( - errorMessage); - break; - - case HttpStatusCode.NotFound: - exception = new NotFoundException( - errorMessage); - break; - - case HttpStatusCode.InternalServerError: - exception = new InternalServerErrorException( - errorMessage, response.Headers.RetryAfter); - break; - - default: - exception = new SignhostRestApiClientException( - errorMessage); - break; - } - - if (exception is SignhostRestApiClientException signhostException) { - signhostException.ResponseBody = responseBody; - } - - throw exception; + var response = await responseTask.ConfigureAwait(false); + + if (response.IsSuccessStatusCode) { + return response; + } + + if (expectedStatusCodes.Contains(response.StatusCode)) { + return response; + } + + string errorType = string.Empty; + string errorMessage = "Unknown Signhost error"; + string responseBody = string.Empty; + + if (response.Content != null) { + responseBody = await response.Content.ReadAsStringAsync() + .ConfigureAwait(false); + + var error = JsonSerializer.Deserialize(responseBody); + + errorType = error?.Type ?? string.Empty; + errorMessage = error?.Message ?? "Unknown Signhost error"; } - private class ErrorResponse - { - [JsonPropertyName("type")] - public string Type { get; set; } + // TO-DO: Use switch pattern in v5 + Exception exception = null; + switch (response.StatusCode) { + case HttpStatusCode.Unauthorized: + exception = new UnauthorizedAccessException( + errorMessage); + break; + + case HttpStatusCode.BadRequest: + exception = new BadRequestException( + errorMessage); + break; + + case HttpStatusCode.PaymentRequired + when errorType == OutOfCreditsApiProblemType: + exception = new OutOfCreditsException( + errorMessage); + break; + + case HttpStatusCode.NotFound: + exception = new NotFoundException( + errorMessage); + break; + + case HttpStatusCode.InternalServerError: + exception = new InternalServerErrorException( + errorMessage, response.Headers.RetryAfter); + break; + + default: + exception = new SignhostRestApiClientException( + errorMessage); + break; + } - [JsonPropertyName("message")] - public string Message { get; set; } + if (exception is SignhostRestApiClientException signhostException) { + signhostException.ResponseBody = responseBody; } + + throw exception; + } + + private class ErrorResponse + { + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("message")] + public string Message { get; set; } } } diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/InternalServerErrorException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/InternalServerErrorException.cs index 0100e317..265489f0 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/InternalServerErrorException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/InternalServerErrorException.cs @@ -1,48 +1,47 @@ using System; using System.Net.Http.Headers; -namespace Signhost.APIClient.Rest.ErrorHandling +namespace Signhost.APIClient.Rest.ErrorHandling; + +[Serializable] +public class InternalServerErrorException + : SignhostRestApiClientException { - [Serializable] - public class InternalServerErrorException - : SignhostRestApiClientException + public InternalServerErrorException() + : base() { - public InternalServerErrorException() - : base() - { - } + } - public InternalServerErrorException(string message) - : base(message) - { - } + public InternalServerErrorException(string message) + : base(message) + { + } - public InternalServerErrorException( - string message, RetryConditionHeaderValue retryAfter) - : base(message) - { - HelpLink = "https://api.signhost.com/Help"; + public InternalServerErrorException( + string message, RetryConditionHeaderValue retryAfter) + : base(message) + { + HelpLink = "https://api.signhost.com/Help"; - if (retryAfter != null) { - if (retryAfter.Date != null) { - RetryAfter = retryAfter.Date; - } + if (retryAfter != null) { + if (retryAfter.Date != null) { + RetryAfter = retryAfter.Date; + } - if (retryAfter.Delta != null) { - RetryAfter = DateTime.Now + retryAfter.Delta; - } + if (retryAfter.Delta != null) { + RetryAfter = DateTime.Now + retryAfter.Delta; } } + } #if SERIALIZABLE - protected InternalServerErrorException( - SerializationInfo info, - StreamingContext context) - : base(info, context) - { - } + protected InternalServerErrorException( + SerializationInfo info, + StreamingContext context) + : base(info, context) + { + } #endif - private DateTimeOffset? RetryAfter { get; set; } - } + private DateTimeOffset? RetryAfter { get; set; } } diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/NotFoundException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/NotFoundException.cs index 6c9eda47..0b61c9e3 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/NotFoundException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/NotFoundException.cs @@ -1,34 +1,33 @@ using System; -namespace Signhost.APIClient.Rest.ErrorHandling +namespace Signhost.APIClient.Rest.ErrorHandling; + +[Serializable] +public class NotFoundException + : SignhostRestApiClientException { - [Serializable] - public class NotFoundException - : SignhostRestApiClientException + public NotFoundException() + : base() { - public NotFoundException() - : base() - { - } + } - public NotFoundException(string message) - : base(message) - { - } + public NotFoundException(string message) + : base(message) + { + } - public NotFoundException(string message, Exception innerException) - : base(message, innerException) - { - HelpLink = "https://api.signhost.com/Help"; - } + public NotFoundException(string message, Exception innerException) + : base(message, innerException) + { + HelpLink = "https://api.signhost.com/Help"; + } #if SERIALIZABLE - protected NotFoundException( - SerializationInfo info, - StreamingContext context) - : base(info, context) - { - } -#endif + protected NotFoundException( + SerializationInfo info, + StreamingContext context) + : base(info, context) + { } +#endif } diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/OutOfCreditsException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/OutOfCreditsException.cs index a5cf42e7..7d8c89e6 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/OutOfCreditsException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/OutOfCreditsException.cs @@ -1,21 +1,20 @@ using System; -namespace Signhost.APIClient.Rest.ErrorHandling +namespace Signhost.APIClient.Rest.ErrorHandling; + +/// +/// An exception which indicates payment is required when an API action is called. +/// +[Serializable] +public class OutOfCreditsException + : SignhostRestApiClientException { /// - /// An exception which indicates payment is required when an API action is called. + /// Initializes a new instance of the class. /// - [Serializable] - public class OutOfCreditsException - : SignhostRestApiClientException + /// The exception message. + public OutOfCreditsException(string message) + : base(message) { - /// - /// Initializes a new instance of the class. - /// - /// The exception message. - public OutOfCreditsException(string message) - : base(message) - { - } } } diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs index e437036d..6e30ba82 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs @@ -1,28 +1,28 @@ using System; -namespace Signhost.APIClient.Rest.ErrorHandling +namespace Signhost.APIClient.Rest.ErrorHandling; + +[Serializable] +public class SignhostRestApiClientException + : Exception { - [Serializable] - public class SignhostRestApiClientException - : Exception + public SignhostRestApiClientException() + : base() { - public SignhostRestApiClientException() - : base() - { - } + } - public SignhostRestApiClientException(string message) - : base(message) - { - } + public SignhostRestApiClientException(string message) + : base(message) + { + } - public SignhostRestApiClientException( - string message, - Exception innerException) - : base(message, innerException) - { - HelpLink = "https://api.signhost.com/Help"; - } + public SignhostRestApiClientException( + string message, + Exception innerException) + : base(message, innerException) + { + HelpLink = "https://api.signhost.com/Help"; + } #if SERIALIZABLE protected SignhostRestApiClientException( @@ -33,9 +33,8 @@ protected SignhostRestApiClientException( } #endif - /// - /// Gets or sets the response body returned from the Signhost REST API. - /// - public string ResponseBody { get; set; } - } + /// + /// Gets or sets the response body returned from the Signhost REST API. + /// + public string ResponseBody { get; set; } } diff --git a/src/SignhostAPIClient/Rest/FileDigestOptions.cs b/src/SignhostAPIClient/Rest/FileDigestOptions.cs index b71e0eb7..bc0218b6 100644 --- a/src/SignhostAPIClient/Rest/FileDigestOptions.cs +++ b/src/SignhostAPIClient/Rest/FileDigestOptions.cs @@ -1,27 +1,26 @@ -namespace Signhost.APIClient.Rest +namespace Signhost.APIClient.Rest; + +/// +/// File digest options for file uploads +/// +public class FileDigestOptions { /// - /// File digest options for file uploads + /// Gets or sets whether to use the Digest header with a checksum of + /// the uploaded file. /// - public class FileDigestOptions - { - /// - /// Gets or sets whether to use the Digest header with a checksum of - /// the uploaded file. - /// - public bool UseFileDigesting { get; set; } = true; + public bool UseFileDigesting { get; set; } = true; - /// - /// Gets or sets the digest algorithm to use when calculating - /// the hash value or the digest algorithm that is used - /// to set the . - /// - public string DigestHashAlgorithm { get; set; } = "SHA-256"; + /// + /// Gets or sets the digest algorithm to use when calculating + /// the hash value or the digest algorithm that is used + /// to set the . + /// + public string DigestHashAlgorithm { get; set; } = "SHA-256"; - /// - /// Gets or sets the hash digest value, you can set this yourself - /// if you know the digest value in advance. - /// - public byte[] DigestHashValue { get; set; } - } + /// + /// Gets or sets the hash digest value, you can set this yourself + /// if you know the digest value in advance. + /// + public byte[] DigestHashValue { get; set; } } diff --git a/src/SignhostAPIClient/Rest/FileUploadOptions.cs b/src/SignhostAPIClient/Rest/FileUploadOptions.cs index 340ad1ec..4336b6d2 100644 --- a/src/SignhostAPIClient/Rest/FileUploadOptions.cs +++ b/src/SignhostAPIClient/Rest/FileUploadOptions.cs @@ -1,14 +1,13 @@ -namespace Signhost.APIClient.Rest +namespace Signhost.APIClient.Rest; + +/// +/// Options to be used during a file upload +/// +public class FileUploadOptions { /// - /// Options to be used during a file upload + /// Gets or sets the . /// - public class FileUploadOptions - { - /// - /// Gets or sets the . - /// - public FileDigestOptions DigestOptions { get; set; } - = new FileDigestOptions(); - } + public FileDigestOptions DigestOptions { get; set; } + = new FileDigestOptions(); } diff --git a/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs b/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs index b7e2836e..3dfa535f 100644 --- a/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs +++ b/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs @@ -2,30 +2,29 @@ using System.Text.Json; using System.Threading.Tasks; -namespace Signhost.APIClient.Rest +namespace Signhost.APIClient.Rest; + +/// +/// Extension methods around JSON Deserialization. +/// +internal static class HttpContentJsonExtensions { /// - /// Extension methods around JSON Deserialization. + /// Reads the JSON content and returns the deserialized value. /// - internal static class HttpContentJsonExtensions + /// Type to deserialize to + /// to read. + /// A deserialized value of + /// or default(T) if no content is available. + internal static async Task FromJsonAsync( + this HttpContent httpContent) { - /// - /// Reads the JSON content and returns the deserialized value. - /// - /// Type to deserialize to - /// to read. - /// A deserialized value of - /// or default(T) if no content is available. - internal static async Task FromJsonAsync( - this HttpContent httpContent) - { - if (httpContent == null) { - return default(T); - } - - var json = await httpContent.ReadAsStringAsync() - .ConfigureAwait(false); - return JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); + if (httpContent == null) { + return default(T); } + + var json = await httpContent.ReadAsStringAsync() + .ConfigureAwait(false); + return JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); } } diff --git a/src/SignhostAPIClient/Rest/ISignHostApiClient.cs b/src/SignhostAPIClient/Rest/ISignHostApiClient.cs index 79c5a885..d2ac880c 100644 --- a/src/SignhostAPIClient/Rest/ISignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/ISignHostApiClient.cs @@ -3,248 +3,247 @@ using System.Threading.Tasks; using Signhost.APIClient.Rest.DataObjects; -namespace Signhost.APIClient.Rest +namespace Signhost.APIClient.Rest; + +/// +/// Interface abstracting the available Signhost API calls. +/// +public interface ISignhostApiClient { /// - /// Interface abstracting the available Signhost API calls. - /// - public interface ISignhostApiClient - { - /// - /// Creates a new transaction. - /// - /// A transaction model. - /// A transaction object. - Task CreateTransactionAsync(Transaction transaction); - - /// - /// Creates a new transaction. - /// - /// A transaction model. - /// A cancellation token. - /// A transaction object. - Task CreateTransactionAsync( - Transaction transaction, - CancellationToken cancellationToken = default); - - /// - /// Adds meta data for a file to an existing transaction by providing a - /// file location and a transaction id. - /// - /// Meta data for the file. - /// A valid transaction Id of an existing transaction. - /// An Id for the file. Should be the same as the fileId in the . - /// A task. - /// Make sure to call this method before - /// . - Task AddOrReplaceFileMetaToTransactionAsync( - FileMeta fileMeta, - string transactionId, - string fileId); - - /// - /// Adds meta data for a file to an existing transaction by providing a - /// file location and a transaction id. - /// - /// Meta data for the file. - /// A valid transaction Id of an existing transaction. - /// An Id for the file. Should be the same as the fileId in the . - /// A cancellation token. - /// A task. - /// Make sure to call this method before - /// . - Task AddOrReplaceFileMetaToTransactionAsync( - FileMeta fileMeta, - string transactionId, - string fileId, - CancellationToken cancellationToken = default); - - /// - /// Add a file to a existing transaction by providing a file location - /// and a transaction id. - /// - /// A Stream containing the file to upload. - /// A valid transaction Id of an existing transaction. - /// A Id for the file. Using the file name is recommended. - /// If a file with the same fileId allready exists the file wil be replaced. - /// . - /// A Task. - Task AddOrReplaceFileToTransactionAsync( - Stream fileStream, - string transactionId, - string fileId, - FileUploadOptions uploadOptions); - - /// - /// Add a file to a existing transaction by providing a file location - /// and a transaction id. - /// - /// A Stream containing the file to upload. - /// A valid transaction Id of an existing transaction. - /// A Id for the file. Using the file name is recommended. - /// If a file with the same fileId allready exists the file wil be replaced. - /// . - /// A cancellation token. - /// A Task. - Task AddOrReplaceFileToTransactionAsync( - Stream fileStream, - string transactionId, - string fileId, - FileUploadOptions uploadOptions, - CancellationToken cancellationToken = default); - - /// - /// Add a file to a existing transaction by providing a file location - /// and a transaction id. - /// - /// A string representation of the file path. - /// A valid transaction Id of an existing transaction. - /// A Id for the file. Using the file name is recommended. - /// If a file with the same fileId allready exists the file wil be replaced. - /// Optional . - /// A Task. - Task AddOrReplaceFileToTransactionAsync( - string filePath, - string transactionId, - string fileId, - FileUploadOptions uploadOptions); - - /// - /// Add a file to a existing transaction by providing a file location - /// and a transaction id. - /// - /// A string representation of the file path. - /// A valid transaction Id of an existing transaction. - /// A Id for the file. Using the file name is recommended. - /// If a file with the same fileId allready exists the file wil be replaced. - /// Optional . - /// A cancellation token. - /// A Task. - Task AddOrReplaceFileToTransactionAsync( - string filePath, - string transactionId, - string fileId, - FileUploadOptions uploadOptions, - CancellationToken cancellationToken = default); - - /// - /// start a existing transaction by providing transaction id. - /// - /// A valid transaction Id of an existing transaction. - /// A Task. - Task StartTransactionAsync(string transactionId); - - /// - /// start a existing transaction by providing transaction id. - /// - /// A valid transaction Id of an existing transaction. - /// A cancellation token. - /// A Task. - Task StartTransactionAsync( - string transactionId, - CancellationToken cancellationToken = default); - - /// - /// Gets an exisiting transaction by providing a transaction id. - /// - /// A valid transaction id for an existing transaction. - /// A object. - Task GetTransactionAsync(string transactionId); - - /// - /// Gets an exisiting transaction by providing a transaction id. - /// - /// A valid transaction id for an existing transaction. - /// A cancellation token. - /// A object. - Task GetTransactionAsync( - string transactionId, - CancellationToken cancellationToken = default); - - /// - /// Gets a existing transaction by providing a transaction id. - /// - /// A valid transaction Id of an existing transaction. - /// A object. - Task> GetTransactionResponseAsync(string transactionId); - - /// - /// Gets a existing transaction by providing a transaction id. - /// - /// A valid transaction Id of an existing transaction. - /// A cancellation token. - /// A object. - Task> GetTransactionResponseAsync( - string transactionId, - CancellationToken cancellationToken = default); - - /// - /// Deletes a existing transaction by providing a transaction id. - /// - /// A valid transaction Id of an existing transaction. - /// A cancellation token. - /// A Task. - Task DeleteTransactionAsync( - string transactionId, - CancellationToken cancellationToken = default); - - /// - /// Deletes a existing transaction by providing a transaction id. - /// - /// A valid transaction Id of an existing transaction. - /// Optional . - /// A Task. - Task DeleteTransactionAsync( - string transactionId, - DeleteTransactionOptions options); - - /// - /// Deletes a existing transaction by providing a transaction id. - /// - /// A valid transaction Id of an existing transaction. - /// Optional . - /// A cancellation token. - /// A Task. - Task DeleteTransactionAsync( - string transactionId, - DeleteTransactionOptions options = default, - CancellationToken cancellationToken = default); - - /// - /// Gets the signed document of a finished transaction by providing transaction id. - /// - /// A valid transaction Id of an existing transaction. - /// A valid file Id of a signed document. - /// Returns a stream containing the signed document data. - Task GetDocumentAsync(string transactionId, string fileId); - - /// - /// Gets the signed document of a finished transaction by providing transaction id. - /// - /// A valid transaction Id of an existing transaction. - /// A valid file Id of a signed document. - /// A cancellation token. - /// Returns a stream containing the signed document data. - Task GetDocumentAsync( - string transactionId, - string fileId, - CancellationToken cancellationToken = default); - - /// - /// Gets the receipt of a finished transaction by providing transaction id. - /// - /// A valid transaction Id of an finnished transaction. - /// Returns a stream containing the receipt data. - Task GetReceiptAsync(string transactionId); - - /// - /// Gets the receipt of a finished transaction by providing transaction id. - /// - /// A valid transaction Id of an finnished transaction. - /// A cancellation token. - /// Returns a stream containing the receipt data. - Task GetReceiptAsync( - string transactionId, - CancellationToken cancellationToken = default); - } + /// Creates a new transaction. + /// + /// A transaction model. + /// A transaction object. + Task CreateTransactionAsync(Transaction transaction); + + /// + /// Creates a new transaction. + /// + /// A transaction model. + /// A cancellation token. + /// A transaction object. + Task CreateTransactionAsync( + Transaction transaction, + CancellationToken cancellationToken = default); + + /// + /// Adds meta data for a file to an existing transaction by providing a + /// file location and a transaction id. + /// + /// Meta data for the file. + /// A valid transaction Id of an existing transaction. + /// An Id for the file. Should be the same as the fileId in the . + /// A task. + /// Make sure to call this method before + /// . + Task AddOrReplaceFileMetaToTransactionAsync( + FileMeta fileMeta, + string transactionId, + string fileId); + + /// + /// Adds meta data for a file to an existing transaction by providing a + /// file location and a transaction id. + /// + /// Meta data for the file. + /// A valid transaction Id of an existing transaction. + /// An Id for the file. Should be the same as the fileId in the . + /// A cancellation token. + /// A task. + /// Make sure to call this method before + /// . + Task AddOrReplaceFileMetaToTransactionAsync( + FileMeta fileMeta, + string transactionId, + string fileId, + CancellationToken cancellationToken = default); + + /// + /// Add a file to a existing transaction by providing a file location + /// and a transaction id. + /// + /// A Stream containing the file to upload. + /// A valid transaction Id of an existing transaction. + /// A Id for the file. Using the file name is recommended. + /// If a file with the same fileId allready exists the file wil be replaced. + /// . + /// A Task. + Task AddOrReplaceFileToTransactionAsync( + Stream fileStream, + string transactionId, + string fileId, + FileUploadOptions uploadOptions); + + /// + /// Add a file to a existing transaction by providing a file location + /// and a transaction id. + /// + /// A Stream containing the file to upload. + /// A valid transaction Id of an existing transaction. + /// A Id for the file. Using the file name is recommended. + /// If a file with the same fileId allready exists the file wil be replaced. + /// . + /// A cancellation token. + /// A Task. + Task AddOrReplaceFileToTransactionAsync( + Stream fileStream, + string transactionId, + string fileId, + FileUploadOptions uploadOptions, + CancellationToken cancellationToken = default); + + /// + /// Add a file to a existing transaction by providing a file location + /// and a transaction id. + /// + /// A string representation of the file path. + /// A valid transaction Id of an existing transaction. + /// A Id for the file. Using the file name is recommended. + /// If a file with the same fileId allready exists the file wil be replaced. + /// Optional . + /// A Task. + Task AddOrReplaceFileToTransactionAsync( + string filePath, + string transactionId, + string fileId, + FileUploadOptions uploadOptions); + + /// + /// Add a file to a existing transaction by providing a file location + /// and a transaction id. + /// + /// A string representation of the file path. + /// A valid transaction Id of an existing transaction. + /// A Id for the file. Using the file name is recommended. + /// If a file with the same fileId allready exists the file wil be replaced. + /// Optional . + /// A cancellation token. + /// A Task. + Task AddOrReplaceFileToTransactionAsync( + string filePath, + string transactionId, + string fileId, + FileUploadOptions uploadOptions, + CancellationToken cancellationToken = default); + + /// + /// start a existing transaction by providing transaction id. + /// + /// A valid transaction Id of an existing transaction. + /// A Task. + Task StartTransactionAsync(string transactionId); + + /// + /// start a existing transaction by providing transaction id. + /// + /// A valid transaction Id of an existing transaction. + /// A cancellation token. + /// A Task. + Task StartTransactionAsync( + string transactionId, + CancellationToken cancellationToken = default); + + /// + /// Gets an exisiting transaction by providing a transaction id. + /// + /// A valid transaction id for an existing transaction. + /// A object. + Task GetTransactionAsync(string transactionId); + + /// + /// Gets an exisiting transaction by providing a transaction id. + /// + /// A valid transaction id for an existing transaction. + /// A cancellation token. + /// A object. + Task GetTransactionAsync( + string transactionId, + CancellationToken cancellationToken = default); + + /// + /// Gets a existing transaction by providing a transaction id. + /// + /// A valid transaction Id of an existing transaction. + /// A object. + Task> GetTransactionResponseAsync(string transactionId); + + /// + /// Gets a existing transaction by providing a transaction id. + /// + /// A valid transaction Id of an existing transaction. + /// A cancellation token. + /// A object. + Task> GetTransactionResponseAsync( + string transactionId, + CancellationToken cancellationToken = default); + + /// + /// Deletes a existing transaction by providing a transaction id. + /// + /// A valid transaction Id of an existing transaction. + /// A cancellation token. + /// A Task. + Task DeleteTransactionAsync( + string transactionId, + CancellationToken cancellationToken = default); + + /// + /// Deletes a existing transaction by providing a transaction id. + /// + /// A valid transaction Id of an existing transaction. + /// Optional . + /// A Task. + Task DeleteTransactionAsync( + string transactionId, + DeleteTransactionOptions options); + + /// + /// Deletes a existing transaction by providing a transaction id. + /// + /// A valid transaction Id of an existing transaction. + /// Optional . + /// A cancellation token. + /// A Task. + Task DeleteTransactionAsync( + string transactionId, + DeleteTransactionOptions options = default, + CancellationToken cancellationToken = default); + + /// + /// Gets the signed document of a finished transaction by providing transaction id. + /// + /// A valid transaction Id of an existing transaction. + /// A valid file Id of a signed document. + /// Returns a stream containing the signed document data. + Task GetDocumentAsync(string transactionId, string fileId); + + /// + /// Gets the signed document of a finished transaction by providing transaction id. + /// + /// A valid transaction Id of an existing transaction. + /// A valid file Id of a signed document. + /// A cancellation token. + /// Returns a stream containing the signed document data. + Task GetDocumentAsync( + string transactionId, + string fileId, + CancellationToken cancellationToken = default); + + /// + /// Gets the receipt of a finished transaction by providing transaction id. + /// + /// A valid transaction Id of an finnished transaction. + /// Returns a stream containing the receipt data. + Task GetReceiptAsync(string transactionId); + + /// + /// Gets the receipt of a finished transaction by providing transaction id. + /// + /// A valid transaction Id of an finnished transaction. + /// A cancellation token. + /// Returns a stream containing the receipt data. + Task GetReceiptAsync( + string transactionId, + CancellationToken cancellationToken = default); } diff --git a/src/SignhostAPIClient/Rest/ISignhostApiClientSettings.cs b/src/SignhostAPIClient/Rest/ISignhostApiClientSettings.cs index 34c7c920..22baf84a 100644 --- a/src/SignhostAPIClient/Rest/ISignhostApiClientSettings.cs +++ b/src/SignhostAPIClient/Rest/ISignhostApiClientSettings.cs @@ -1,26 +1,25 @@ using System; -namespace Signhost.APIClient.Rest -{ - public delegate void AddHeaders(string name, string value); +namespace Signhost.APIClient.Rest; + +public delegate void AddHeaders(string name, string value); - public interface ISignhostApiClientSettings - { - /// - /// Gets the usertoken identifying an authorized user. - /// - string UserToken { get; } +public interface ISignhostApiClientSettings +{ + /// + /// Gets the usertoken identifying an authorized user. + /// + string UserToken { get; } - /// - /// Gets the app key of your applications. - /// - string APPKey { get; } + /// + /// Gets the app key of your applications. + /// + string APPKey { get; } - /// - /// Gets the signhost API endpoint. - /// - string Endpoint { get; } + /// + /// Gets the signhost API endpoint. + /// + string Endpoint { get; } - Action AddHeader { get; } - } + Action AddHeader { get; } } diff --git a/src/SignhostAPIClient/Rest/ISignhostApiReceiver.cs b/src/SignhostAPIClient/Rest/ISignhostApiReceiver.cs index 7778debe..ba4eb961 100644 --- a/src/SignhostAPIClient/Rest/ISignhostApiReceiver.cs +++ b/src/SignhostAPIClient/Rest/ISignhostApiReceiver.cs @@ -1,23 +1,22 @@ using System.Collections.Generic; using Signhost.APIClient.Rest.DataObjects; -namespace Signhost.APIClient.Rest +namespace Signhost.APIClient.Rest; + +/// +/// Interface abstracting the available Signhost API responses. +/// +public interface ISignhostApiReceiver { /// - /// Interface abstracting the available Signhost API responses. + /// Checks the validity of the postback checksum. /// - public interface ISignhostApiReceiver - { - /// - /// Checks the validity of the postback checksum. - /// - /// true, if postback checksum valid was validated, false otherwise. - /// HTTP response headers. - /// HTTP response body. - /// A transaction object. - bool IsPostbackChecksumValid( - IDictionary headers, - string body, - out Transaction postbackTransaction); - } + /// true, if postback checksum valid was validated, false otherwise. + /// HTTP response headers. + /// HTTP response body. + /// A transaction object. + bool IsPostbackChecksumValid( + IDictionary headers, + string body, + out Transaction postbackTransaction); } diff --git a/src/SignhostAPIClient/Rest/JsonContent.cs b/src/SignhostAPIClient/Rest/JsonContent.cs index 837d627e..cb31cea6 100644 --- a/src/SignhostAPIClient/Rest/JsonContent.cs +++ b/src/SignhostAPIClient/Rest/JsonContent.cs @@ -2,45 +2,44 @@ using System.Net.Http.Headers; using System.Text.Json; -namespace Signhost.APIClient.Rest +namespace Signhost.APIClient.Rest; + +/// +/// See the helper class. +/// +internal static class JsonContent { /// - /// See the helper class. + /// Creates a new . /// - internal static class JsonContent + /// Type to serialize. + /// Value to serialize. + /// . + internal static JsonContent From(T value) { - /// - /// Creates a new . - /// - /// Type to serialize. - /// Value to serialize. - /// . - internal static JsonContent From(T value) - { - return new JsonContent(value); - } + return new JsonContent(value); } +} +/// +/// A class for application/json. +/// +/// The type to serialize +internal class JsonContent + : StringContent +{ /// - /// A class for application/json. + /// Initializes a new instance of the class. /// - /// The type to serialize - internal class JsonContent - : StringContent + /// Value to serialize. + public JsonContent(T value) + : base(ToJson(value)) { - /// - /// Initializes a new instance of the class. - /// - /// Value to serialize. - public JsonContent(T value) - : base(ToJson(value)) - { - Headers.ContentType = new MediaTypeHeaderValue("application/json"); - } + Headers.ContentType = new MediaTypeHeaderValue("application/json"); + } - private static string ToJson(T value) - { - return JsonSerializer.Serialize(value, SignhostJsonSerializerOptions.Default); - } + private static string ToJson(T value) + { + return JsonSerializer.Serialize(value, SignhostJsonSerializerOptions.Default); } } diff --git a/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs b/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs index 07c20313..06b9c0b8 100644 --- a/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs +++ b/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs @@ -3,54 +3,53 @@ using System.Text.Json.Serialization; using Signhost.APIClient.Rest.DataObjects; -namespace Signhost.APIClient.Rest.JsonConverters +namespace Signhost.APIClient.Rest.JsonConverters; + +/// +/// JSON converter factory for converting the enum. +/// Invalid values are mapped to . +/// +internal class LevelEnumConverter + : JsonConverter { - /// - /// JSON converter factory for converting the enum. - /// Invalid values are mapped to . - /// - internal class LevelEnumConverter - : JsonConverter + public override Level? Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) { - public override Level? Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.Null) { - return null; - } - - if (reader.TokenType == JsonTokenType.String) { - var value = reader.GetString() ?? string.Empty; - if (Enum.TryParse(value, out var level)) { - return level; - } - - return Level.Unknown; - } + if (reader.TokenType == JsonTokenType.Null) { + return null; + } - if (reader.TokenType == JsonTokenType.Number) { - int value = reader.GetInt32(); - if (Enum.IsDefined(typeof(Level), value)) { - return (Level)value; - } + if (reader.TokenType == JsonTokenType.String) { + var value = reader.GetString() ?? string.Empty; + if (Enum.TryParse(value, out var level)) { + return level; } return Level.Unknown; } - public override void Write( - Utf8JsonWriter writer, - Level? value, - JsonSerializerOptions options) - { - if (value is null) { - writer.WriteNullValue(); - } - else { - writer.WriteStringValue(value.ToString()); + if (reader.TokenType == JsonTokenType.Number) { + int value = reader.GetInt32(); + if (Enum.IsDefined(typeof(Level), value)) { + return (Level)value; } } + + return Level.Unknown; + } + + public override void Write( + Utf8JsonWriter writer, + Level? value, + JsonSerializerOptions options) + { + if (value is null) { + writer.WriteNullValue(); + } + else { + writer.WriteStringValue(value.ToString()); + } } } diff --git a/src/SignhostAPIClient/Rest/SignHostApiClient.cs b/src/SignhostAPIClient/Rest/SignHostApiClient.cs index a453f2cb..4c7f698b 100644 --- a/src/SignhostAPIClient/Rest/SignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/SignHostApiClient.cs @@ -9,475 +9,474 @@ using Signhost.APIClient.Rest.DataObjects; using Signhost.APIClient.Rest.ErrorHandling; -namespace Signhost.APIClient.Rest +namespace Signhost.APIClient.Rest; + +/// +/// Implements the interface which provides +/// an signhost api client implementation. +/// +public class SignhostApiClient + : ISignhostApiClient + , IDisposable { + private const string ApiVersion = "v1"; + + private static readonly string Version = typeof(SignhostApiClient) +#if TYPEINFO + .GetTypeInfo() +#endif + .Assembly.GetCustomAttribute() + .Version; + + private readonly ISignhostApiClientSettings settings; + private readonly HttpClient client; + /// - /// Implements the interface which provides - /// an signhost api client implementation. + /// Initializes a new instance of the class. + /// Set your usertoken and APPKey by creating a . /// - public class SignhostApiClient - : ISignhostApiClient - , IDisposable + /// . + public SignhostApiClient(ISignhostApiClientSettings settings) + : this(settings, new HttpClient()) { - private const string ApiVersion = "v1"; + } - private static readonly string Version = typeof(SignhostApiClient) -#if TYPEINFO - .GetTypeInfo() -#endif - .Assembly.GetCustomAttribute() - .Version; - - private readonly ISignhostApiClientSettings settings; - private readonly HttpClient client; - - /// - /// Initializes a new instance of the class. - /// Set your usertoken and APPKey by creating a . - /// - /// . - public SignhostApiClient(ISignhostApiClientSettings settings) - : this(settings, new HttpClient()) - { + /// + /// Initializes a new instance of the class. + /// Set your usertoken and APPKey by creating a . + /// + /// . + /// to use for all http calls. + public SignhostApiClient( + ISignhostApiClientSettings settings, + HttpClient httpClient) + { + this.settings = settings; + this.client = httpClient; + this.client.BaseAddress = new Uri( + settings.Endpoint + (settings.Endpoint.EndsWith("/") ? string.Empty : "/")); + this.client.DefaultRequestHeaders.UserAgent.Add( + new System.Net.Http.Headers.ProductInfoHeaderValue( + "SignhostClientLibrary", + Version)); + this.client.DefaultRequestHeaders.Add("Application", ApplicationHeader); + + if (!string.IsNullOrWhiteSpace(settings.UserToken)) { + this.client.DefaultRequestHeaders.Add("Authorization", AuthorizationHeader); } - /// - /// Initializes a new instance of the class. - /// Set your usertoken and APPKey by creating a . - /// - /// . - /// to use for all http calls. - public SignhostApiClient( - ISignhostApiClientSettings settings, - HttpClient httpClient) - { - this.settings = settings; - this.client = httpClient; - this.client.BaseAddress = new Uri( - settings.Endpoint + (settings.Endpoint.EndsWith("/") ? string.Empty : "/")); - this.client.DefaultRequestHeaders.UserAgent.Add( - new System.Net.Http.Headers.ProductInfoHeaderValue( - "SignhostClientLibrary", - Version)); - this.client.DefaultRequestHeaders.Add("Application", ApplicationHeader); - - if (!string.IsNullOrWhiteSpace(settings.UserToken)) { - this.client.DefaultRequestHeaders.Add("Authorization", AuthorizationHeader); - } - - this.client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse($"application/vnd.signhost.{ApiVersion}+json")); - settings.AddHeader?.Invoke(this.client.DefaultRequestHeaders.Add); + this.client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse($"application/vnd.signhost.{ApiVersion}+json")); + settings.AddHeader?.Invoke(this.client.DefaultRequestHeaders.Add); + } + + private string ApplicationHeader + => $"APPKey {settings.APPKey}"; + + private string AuthorizationHeader + => $"APIKey {settings.UserToken}"; + + /// + public async Task CreateTransactionAsync( + Transaction transaction) + => await CreateTransactionAsync(transaction, default) + .ConfigureAwait(false); + + /// + public async Task CreateTransactionAsync( + Transaction transaction, + CancellationToken cancellationToken = default) + { + if (transaction == null) { + throw new ArgumentNullException(nameof(transaction)); } - private string ApplicationHeader - => $"APPKey {settings.APPKey}"; + var result = await client + .PostAsync( + "transaction", + JsonContent.From(transaction), + cancellationToken) + .EnsureSignhostSuccessStatusCodeAsync() + .ConfigureAwait(false); - private string AuthorizationHeader - => $"APIKey {settings.UserToken}"; + return await result.Content.FromJsonAsync() + .ConfigureAwait(false); + } - /// - public async Task CreateTransactionAsync( - Transaction transaction) - => await CreateTransactionAsync(transaction, default) - .ConfigureAwait(false); + /// + public async Task> GetTransactionResponseAsync( + string transactionId) + => await GetTransactionResponseAsync(transactionId, default) + .ConfigureAwait(false); - /// - public async Task CreateTransactionAsync( - Transaction transaction, - CancellationToken cancellationToken = default) - { - if (transaction == null) { - throw new ArgumentNullException(nameof(transaction)); - } - - var result = await client - .PostAsync( - "transaction", - JsonContent.From(transaction), - cancellationToken) - .EnsureSignhostSuccessStatusCodeAsync() - .ConfigureAwait(false); + /// + public async Task> GetTransactionResponseAsync( + string transactionId, + CancellationToken cancellationToken = default) + { + if (transactionId == null) { + throw new ArgumentNullException(nameof(transactionId)); + } - return await result.Content.FromJsonAsync() - .ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(transactionId)) { + throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); } - /// - public async Task> GetTransactionResponseAsync( - string transactionId) - => await GetTransactionResponseAsync(transactionId, default) - .ConfigureAwait(false); + var result = await client + .GetAsync( + "transaction".JoinPaths(transactionId), + cancellationToken) + .EnsureSignhostSuccessStatusCodeAsync(HttpStatusCode.Gone) + .ConfigureAwait(false); + var transaction = await result.Content.FromJsonAsync() + .ConfigureAwait(false); - /// - public async Task> GetTransactionResponseAsync( - string transactionId, - CancellationToken cancellationToken = default) - { - if (transactionId == null) { - throw new ArgumentNullException(nameof(transactionId)); - } + return new ApiResponse(result, transaction); + } - if (string.IsNullOrWhiteSpace(transactionId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); - } + /// + public async Task GetTransactionAsync(string transactionId) + => await GetTransactionAsync(transactionId, default) + .ConfigureAwait(false); - var result = await client - .GetAsync( - "transaction".JoinPaths(transactionId), - cancellationToken) - .EnsureSignhostSuccessStatusCodeAsync(HttpStatusCode.Gone) - .ConfigureAwait(false); - var transaction = await result.Content.FromJsonAsync() - .ConfigureAwait(false); + /// + public async Task GetTransactionAsync( + string transactionId, + CancellationToken cancellationToken = default) + { + var response = await GetTransactionResponseAsync( + transactionId, + cancellationToken) + .ConfigureAwait(false); + + response.EnsureAvailableStatusCode(); + + return response.Value; + } + + /// + public async Task DeleteTransactionAsync( + string transactionId, + CancellationToken cancellationToken = default) + => await DeleteTransactionAsync( + transactionId, + default, + cancellationToken).ConfigureAwait(false); + + /// + public async Task DeleteTransactionAsync( + string transactionId, + DeleteTransactionOptions options) + => await DeleteTransactionAsync( + transactionId, + options, + default).ConfigureAwait(false); + + /// + public async Task DeleteTransactionAsync( + string transactionId, + DeleteTransactionOptions options, + CancellationToken cancellationToken = default) + { + if (transactionId == null) { + throw new ArgumentNullException(nameof(transactionId)); + } - return new ApiResponse(result, transaction); + if (string.IsNullOrWhiteSpace(transactionId)) { + throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); } - /// - public async Task GetTransactionAsync(string transactionId) - => await GetTransactionAsync(transactionId, default) - .ConfigureAwait(false); + if (options == null) { + options = new DeleteTransactionOptions(); + } - /// - public async Task GetTransactionAsync( - string transactionId, - CancellationToken cancellationToken = default) - { - var response = await GetTransactionResponseAsync( - transactionId, + var request = new HttpRequestMessage(HttpMethod.Delete, "transaction".JoinPaths(transactionId)); + request.Content = JsonContent.From(options); + await client + .SendAsync( + request, cancellationToken) - .ConfigureAwait(false); + .EnsureSignhostSuccessStatusCodeAsync() + .ConfigureAwait(false); + } - response.EnsureAvailableStatusCode(); + /// + public async Task AddOrReplaceFileMetaToTransactionAsync( + FileMeta fileMeta, + string transactionId, + string fileId) + => await AddOrReplaceFileMetaToTransactionAsync( + fileMeta, + transactionId, + fileId, + default).ConfigureAwait(false); + + /// + public async Task AddOrReplaceFileMetaToTransactionAsync( + FileMeta fileMeta, + string transactionId, + string fileId, + CancellationToken cancellationToken = default) + { + if (fileMeta == null) { + throw new ArgumentNullException("fileMeta"); + } - return response.Value; + if (transactionId == null) { + throw new ArgumentNullException(nameof(transactionId)); } - /// - public async Task DeleteTransactionAsync( - string transactionId, - CancellationToken cancellationToken = default) - => await DeleteTransactionAsync( - transactionId, - default, - cancellationToken).ConfigureAwait(false); - - /// - public async Task DeleteTransactionAsync( - string transactionId, - DeleteTransactionOptions options) - => await DeleteTransactionAsync( - transactionId, - options, - default).ConfigureAwait(false); - - /// - public async Task DeleteTransactionAsync( - string transactionId, - DeleteTransactionOptions options, - CancellationToken cancellationToken = default) - { - if (transactionId == null) { - throw new ArgumentNullException(nameof(transactionId)); - } - - if (string.IsNullOrWhiteSpace(transactionId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); - } - - if (options == null) { - options = new DeleteTransactionOptions(); - } - - var request = new HttpRequestMessage(HttpMethod.Delete, "transaction".JoinPaths(transactionId)); - request.Content = JsonContent.From(options); - await client - .SendAsync( - request, - cancellationToken) - .EnsureSignhostSuccessStatusCodeAsync() - .ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(transactionId)) { + throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); } - /// - public async Task AddOrReplaceFileMetaToTransactionAsync( - FileMeta fileMeta, - string transactionId, - string fileId) - => await AddOrReplaceFileMetaToTransactionAsync( - fileMeta, - transactionId, - fileId, - default).ConfigureAwait(false); - - /// - public async Task AddOrReplaceFileMetaToTransactionAsync( - FileMeta fileMeta, - string transactionId, - string fileId, - CancellationToken cancellationToken = default) - { - if (fileMeta == null) { - throw new ArgumentNullException("fileMeta"); - } - - if (transactionId == null) { - throw new ArgumentNullException(nameof(transactionId)); - } - - if (string.IsNullOrWhiteSpace(transactionId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); - } - - if (fileId == null) { - throw new ArgumentNullException(nameof(fileId)); - } - - if (string.IsNullOrWhiteSpace(fileId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(fileId)); - } - - await client - .PutAsync( - "transaction".JoinPaths(transactionId, "file", fileId), - JsonContent.From(fileMeta), - cancellationToken) - .EnsureSignhostSuccessStatusCodeAsync() - .ConfigureAwait(false); + if (fileId == null) { + throw new ArgumentNullException(nameof(fileId)); + } + + if (string.IsNullOrWhiteSpace(fileId)) { + throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(fileId)); } - /// - public async Task AddOrReplaceFileToTransactionAsync( + await client + .PutAsync( + "transaction".JoinPaths(transactionId, "file", fileId), + JsonContent.From(fileMeta), + cancellationToken) + .EnsureSignhostSuccessStatusCodeAsync() + .ConfigureAwait(false); + } + + /// + public async Task AddOrReplaceFileToTransactionAsync( + Stream fileStream, + string transactionId, + string fileId, + FileUploadOptions uploadOptions) + => await AddOrReplaceFileToTransactionAsync( + fileStream, + transactionId, + fileId, + uploadOptions, + default).ConfigureAwait(false); + + /// + public async Task AddOrReplaceFileToTransactionAsync( Stream fileStream, string transactionId, string fileId, - FileUploadOptions uploadOptions) - => await AddOrReplaceFileToTransactionAsync( - fileStream, - transactionId, - fileId, - uploadOptions, - default).ConfigureAwait(false); - - /// - public async Task AddOrReplaceFileToTransactionAsync( - Stream fileStream, - string transactionId, - string fileId, - FileUploadOptions uploadOptions, - CancellationToken cancellationToken = default) - { - if (fileStream == null) { - throw new ArgumentNullException(nameof(fileStream)); - } - - if (transactionId == null) { - throw new ArgumentNullException(nameof(transactionId)); - } - - if (string.IsNullOrWhiteSpace(transactionId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); - } - - if (fileId == null) { - throw new ArgumentNullException(nameof(fileId)); - } - - if (string.IsNullOrWhiteSpace(fileId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(fileId)); - } - - if (uploadOptions == null) { - uploadOptions = new FileUploadOptions(); - } - - var content = new StreamContent(fileStream) - .WithDigest(fileStream, uploadOptions.DigestOptions); - content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf"); - - await client - .PutAsync( - "transaction".JoinPaths(transactionId, "file", fileId), - content, - cancellationToken) - .EnsureSignhostSuccessStatusCodeAsync() - .ConfigureAwait(false); + FileUploadOptions uploadOptions, + CancellationToken cancellationToken = default) + { + if (fileStream == null) { + throw new ArgumentNullException(nameof(fileStream)); } - /// - public Task AddOrReplaceFileToTransaction( - Stream fileStream, - string transactionId, - string fileId) - { - return AddOrReplaceFileToTransactionAsync( - fileStream, - transactionId, - fileId, - null); + if (transactionId == null) { + throw new ArgumentNullException(nameof(transactionId)); } - /// - public async Task AddOrReplaceFileToTransactionAsync( - string filePath, - string transactionId, - string fileId, - FileUploadOptions uploadOptions) - => await AddOrReplaceFileToTransactionAsync( - filePath, - transactionId, - fileId, - uploadOptions, - default).ConfigureAwait(false); - - /// - public async Task AddOrReplaceFileToTransactionAsync( - string filePath, - string transactionId, - string fileId, - FileUploadOptions uploadOptions, - CancellationToken cancellationToken = default) - { - if (filePath == null) { - throw new ArgumentNullException(nameof(filePath)); - } - - using (Stream fileStream = System.IO.File.Open( - filePath, - FileMode.Open, - FileAccess.Read, - FileShare.Delete | FileShare.Read)) - { - await AddOrReplaceFileToTransactionAsync( - fileStream, - transactionId, - fileId, - uploadOptions, - cancellationToken) - .ConfigureAwait(false); - } + if (string.IsNullOrWhiteSpace(transactionId)) { + throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); } - /// - public Task AddOrReplaceFileToTransaction( - string filePath, - string transactionId, - string fileId) - { - return AddOrReplaceFileToTransactionAsync( - filePath, - transactionId, - fileId, - null); + if (fileId == null) { + throw new ArgumentNullException(nameof(fileId)); } - /// - public async Task StartTransactionAsync( - string transactionId) - => await StartTransactionAsync(transactionId, default) - .ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(fileId)) { + throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(fileId)); + } + + if (uploadOptions == null) { + uploadOptions = new FileUploadOptions(); + } - /// - public async Task StartTransactionAsync( - string transactionId, - CancellationToken cancellationToken = default) + var content = new StreamContent(fileStream) + .WithDigest(fileStream, uploadOptions.DigestOptions); + content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf"); + + await client + .PutAsync( + "transaction".JoinPaths(transactionId, "file", fileId), + content, + cancellationToken) + .EnsureSignhostSuccessStatusCodeAsync() + .ConfigureAwait(false); + } + + /// + public Task AddOrReplaceFileToTransaction( + Stream fileStream, + string transactionId, + string fileId) + { + return AddOrReplaceFileToTransactionAsync( + fileStream, + transactionId, + fileId, + null); + } + + /// + public async Task AddOrReplaceFileToTransactionAsync( + string filePath, + string transactionId, + string fileId, + FileUploadOptions uploadOptions) + => await AddOrReplaceFileToTransactionAsync( + filePath, + transactionId, + fileId, + uploadOptions, + default).ConfigureAwait(false); + + /// + public async Task AddOrReplaceFileToTransactionAsync( + string filePath, + string transactionId, + string fileId, + FileUploadOptions uploadOptions, + CancellationToken cancellationToken = default) + { + if (filePath == null) { + throw new ArgumentNullException(nameof(filePath)); + } + + using (Stream fileStream = System.IO.File.Open( + filePath, + FileMode.Open, + FileAccess.Read, + FileShare.Delete | FileShare.Read)) { - if (transactionId == null) { - throw new ArgumentNullException(nameof(transactionId)); - } - - if (string.IsNullOrWhiteSpace(transactionId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); - } - - await client - .PutAsync( - "transaction".JoinPaths(transactionId, "start"), - null, + await AddOrReplaceFileToTransactionAsync( + fileStream, + transactionId, + fileId, + uploadOptions, cancellationToken) - .EnsureSignhostSuccessStatusCodeAsync() .ConfigureAwait(false); } + } - /// - public async Task GetReceiptAsync(string transactionId) - => await GetReceiptAsync(transactionId, default) - .ConfigureAwait(false); + /// + public Task AddOrReplaceFileToTransaction( + string filePath, + string transactionId, + string fileId) + { + return AddOrReplaceFileToTransactionAsync( + filePath, + transactionId, + fileId, + null); + } - /// - public async Task GetReceiptAsync( - string transactionId, - CancellationToken cancellationToken = default) - { - if (transactionId == null) { - throw new ArgumentNullException(nameof(transactionId)); - } + /// + public async Task StartTransactionAsync( + string transactionId) + => await StartTransactionAsync(transactionId, default) + .ConfigureAwait(false); - if (string.IsNullOrWhiteSpace(transactionId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); - } + /// + public async Task StartTransactionAsync( + string transactionId, + CancellationToken cancellationToken = default) + { + if (transactionId == null) { + throw new ArgumentNullException(nameof(transactionId)); + } - var result = await client - .GetStreamAsync( - "file".JoinPaths("receipt", transactionId)) - .ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(transactionId)) { + throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); + } + + await client + .PutAsync( + "transaction".JoinPaths(transactionId, "start"), + null, + cancellationToken) + .EnsureSignhostSuccessStatusCodeAsync() + .ConfigureAwait(false); + } - return result; + /// + public async Task GetReceiptAsync(string transactionId) + => await GetReceiptAsync(transactionId, default) + .ConfigureAwait(false); + + /// + public async Task GetReceiptAsync( + string transactionId, + CancellationToken cancellationToken = default) + { + if (transactionId == null) { + throw new ArgumentNullException(nameof(transactionId)); } - /// - public async Task GetDocumentAsync( - string transactionId, - string fileId) - => await GetDocumentAsync(transactionId, fileId, default) - .ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(transactionId)) { + throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); + } - /// - public async Task GetDocumentAsync( - string transactionId, - string fileId, - CancellationToken cancellationToken = default) - { - if (transactionId == null) { - throw new ArgumentNullException(nameof(transactionId)); - } + var result = await client + .GetStreamAsync( + "file".JoinPaths("receipt", transactionId)) + .ConfigureAwait(false); - if (string.IsNullOrWhiteSpace(transactionId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); - } + return result; + } - if (fileId == null) { - throw new ArgumentNullException(nameof(fileId)); - } + /// + public async Task GetDocumentAsync( + string transactionId, + string fileId) + => await GetDocumentAsync(transactionId, fileId, default) + .ConfigureAwait(false); - if (string.IsNullOrWhiteSpace(fileId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(fileId)); - } + /// + public async Task GetDocumentAsync( + string transactionId, + string fileId, + CancellationToken cancellationToken = default) + { + if (transactionId == null) { + throw new ArgumentNullException(nameof(transactionId)); + } - var result = await client - .GetStreamAsync( - "transaction".JoinPaths(transactionId, "file", fileId)) - .ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(transactionId)) { + throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); + } - return result; + if (fileId == null) { + throw new ArgumentNullException(nameof(fileId)); } - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); + if (string.IsNullOrWhiteSpace(fileId)) { + throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(fileId)); } - /// - /// Disposes the instance. - /// - /// Is callled. - protected virtual void Dispose(bool disposing) - { - if (disposing) { - client?.Dispose(); - } + var result = await client + .GetStreamAsync( + "transaction".JoinPaths(transactionId, "file", fileId)) + .ConfigureAwait(false); + + return result; + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Disposes the instance. + /// + /// Is callled. + protected virtual void Dispose(bool disposing) + { + if (disposing) { + client?.Dispose(); } } } diff --git a/src/SignhostAPIClient/Rest/SignHostApiClientSettings.cs b/src/SignhostAPIClient/Rest/SignHostApiClientSettings.cs index b4afed65..c96340d4 100644 --- a/src/SignhostAPIClient/Rest/SignHostApiClientSettings.cs +++ b/src/SignhostAPIClient/Rest/SignHostApiClientSettings.cs @@ -1,29 +1,28 @@ using System; -namespace Signhost.APIClient.Rest +namespace Signhost.APIClient.Rest; + +public class SignhostApiClientSettings + : ISignhostApiClientSettings { - public class SignhostApiClientSettings - : ISignhostApiClientSettings - { - public const string DefaultEndpoint = "https://api.signhost.com/api/"; + public const string DefaultEndpoint = "https://api.signhost.com/api/"; - public SignhostApiClientSettings(string appkey, string userToken) - { - APPKey = appkey; - UserToken = userToken; - } + public SignhostApiClientSettings(string appkey, string userToken) + { + APPKey = appkey; + UserToken = userToken; + } - public SignhostApiClientSettings(string appkey) - { - APPKey = appkey; - } + public SignhostApiClientSettings(string appkey) + { + APPKey = appkey; + } - public string UserToken { get; set; } + public string UserToken { get; set; } - public string APPKey { get; private set; } + public string APPKey { get; private set; } - public string Endpoint { get; set; } = DefaultEndpoint; + public string Endpoint { get; set; } = DefaultEndpoint; - public Action AddHeader { get; set; } - } + public Action AddHeader { get; set; } } diff --git a/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs b/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs index da6c1899..d4fb3bfa 100644 --- a/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs +++ b/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs @@ -7,86 +7,85 @@ using Signhost.APIClient.Rest; using Signhost.APIClient.Rest.DataObjects; -namespace Signhost.APIClient +namespace Signhost.APIClient; + +/// +/// Implements the interface which provides +/// a Signhost API receiver implementation. +/// +public class SignhostApiReceiver + : ISignhostApiReceiver { + private readonly SignhostApiReceiverSettings settings; + /// - /// Implements the interface which provides - /// a Signhost API receiver implementation. + /// Initializes a new instance of the class. + /// Set your SharedSecret by creating a . /// - public class SignhostApiReceiver - : ISignhostApiReceiver + /// + /// Settings for the receiver. + /// + public SignhostApiReceiver(SignhostApiReceiverSettings receiverSettings) { - private readonly SignhostApiReceiverSettings settings; + this.settings = receiverSettings; + } - /// - /// Initializes a new instance of the class. - /// Set your SharedSecret by creating a . - /// - /// - /// Settings for the receiver. - /// - public SignhostApiReceiver(SignhostApiReceiverSettings receiverSettings) - { - this.settings = receiverSettings; - } + /// + public bool IsPostbackChecksumValid( + IDictionary headers, + string body, + out Transaction postbackTransaction) + { + postbackTransaction = null; + string postbackChecksum; + string calculatedChecksum; + PostbackTransaction postback; - /// - public bool IsPostbackChecksumValid( - IDictionary headers, - string body, - out Transaction postbackTransaction) - { - postbackTransaction = null; - string postbackChecksum; - string calculatedChecksum; - PostbackTransaction postback; + postback = DeserializeToPostbackTransaction(body); + postbackChecksum = GetChecksumFromHeadersOrPostback(headers, postback); + bool parametersAreValid = HasValidChecksumProperties(postbackChecksum, postback); - postback = DeserializeToPostbackTransaction(body); - postbackChecksum = GetChecksumFromHeadersOrPostback(headers, postback); - bool parametersAreValid = HasValidChecksumProperties(postbackChecksum, postback); + if (parametersAreValid) { + calculatedChecksum = CalculateChecksumFromPostback(postback); + postbackTransaction = postback; + } else { + return false; + } - if (parametersAreValid) { - calculatedChecksum = CalculateChecksumFromPostback(postback); - postbackTransaction = postback; - } else { - return false; - } + return Equals(calculatedChecksum, postbackChecksum); + } - return Equals(calculatedChecksum, postbackChecksum); + private string CalculateChecksumFromPostback(PostbackTransaction postback) + { + using (var sha1 = SHA1.Create()) { + var checksumBytes = sha1.ComputeHash(Encoding.UTF8.GetBytes( + $"{postback.Id}||{(int)postback.Status}|{settings.SharedSecret}")); + return BitConverter.ToString(checksumBytes) + .Replace("-", string.Empty) + .ToLower(); } + } - private string CalculateChecksumFromPostback(PostbackTransaction postback) - { - using (var sha1 = SHA1.Create()) { - var checksumBytes = sha1.ComputeHash(Encoding.UTF8.GetBytes( - $"{postback.Id}||{(int)postback.Status}|{settings.SharedSecret}")); - return BitConverter.ToString(checksumBytes) - .Replace("-", string.Empty) - .ToLower(); - } - } + private PostbackTransaction DeserializeToPostbackTransaction(string body) + { + return JsonSerializer.Deserialize(body, SignhostJsonSerializerOptions.Default); + } - private PostbackTransaction DeserializeToPostbackTransaction(string body) - { - return JsonSerializer.Deserialize(body, SignhostJsonSerializerOptions.Default); + private string GetChecksumFromHeadersOrPostback( + IDictionary headers, + PostbackTransaction postback) + { + string[] postbackChecksumArray; + if (headers.TryGetValue("Checksum", out postbackChecksumArray)) { + return postbackChecksumArray.First(); } - - private string GetChecksumFromHeadersOrPostback( - IDictionary headers, - PostbackTransaction postback) - { - string[] postbackChecksumArray; - if (headers.TryGetValue("Checksum", out postbackChecksumArray)) { - return postbackChecksumArray.First(); - } - else { - return postback.Checksum; - } + else { + return postback.Checksum; } + } - private bool HasValidChecksumProperties(string postbackChecksum, PostbackTransaction postback) - { - return !string.IsNullOrWhiteSpace(postbackChecksum) && !string.IsNullOrWhiteSpace(postback.Id); - } + private bool HasValidChecksumProperties(string postbackChecksum, PostbackTransaction postback) + { + return !string.IsNullOrWhiteSpace(postbackChecksum) && !string.IsNullOrWhiteSpace(postback.Id); } } diff --git a/src/SignhostAPIClient/Rest/SignhostApiReceiverSettings.cs b/src/SignhostAPIClient/Rest/SignhostApiReceiverSettings.cs index d3d962aa..c1b2bbce 100644 --- a/src/SignhostAPIClient/Rest/SignhostApiReceiverSettings.cs +++ b/src/SignhostAPIClient/Rest/SignhostApiReceiverSettings.cs @@ -1,19 +1,18 @@ -namespace Signhost.APIClient.Rest +namespace Signhost.APIClient.Rest; + +/// +/// Registers the necessary settings for the class. +/// +public class SignhostApiReceiverSettings { - /// - /// Registers the necessary settings for the class. - /// - public class SignhostApiReceiverSettings + public SignhostApiReceiverSettings(string sharedsecret) { - public SignhostApiReceiverSettings(string sharedsecret) - { - SharedSecret = sharedsecret; - } - - /// - /// Gets the shared secret. - /// - /// The shared secret key issued by Signhost.com. - public string SharedSecret { get; private set; } + SharedSecret = sharedsecret; } + + /// + /// Gets the shared secret. + /// + /// The shared secret key issued by Signhost.com. + public string SharedSecret { get; private set; } } diff --git a/src/SignhostAPIClient/Rest/SignhostJsonSerializerOptions.cs b/src/SignhostAPIClient/Rest/SignhostJsonSerializerOptions.cs index 37845021..22c34542 100644 --- a/src/SignhostAPIClient/Rest/SignhostJsonSerializerOptions.cs +++ b/src/SignhostAPIClient/Rest/SignhostJsonSerializerOptions.cs @@ -1,23 +1,22 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Signhost.APIClient.Rest +namespace Signhost.APIClient.Rest; + +/// +/// Centralized JSON serialization options for Signhost API. +/// +public static class SignhostJsonSerializerOptions { /// - /// Centralized JSON serialization options for Signhost API. + /// Gets the default JSON serializer options. /// - public static class SignhostJsonSerializerOptions + public static JsonSerializerOptions Default { get; } = new JsonSerializerOptions { - /// - /// Gets the default JSON serializer options. - /// - public static JsonSerializerOptions Default { get; } = new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - Converters = { - new JsonStringEnumConverter(), - }, - }; - } + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { + new JsonStringEnumConverter(), + }, + }; } diff --git a/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs b/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs index 3ae2389b..eb75441a 100644 --- a/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs +++ b/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs @@ -3,95 +3,94 @@ using System.Net.Http; using System.Security.Cryptography; -namespace Signhost.APIClient.Rest +namespace Signhost.APIClient.Rest; + +/// +/// digest extensions. +/// +public static class StreamContentDigestOptionsExtensions { /// - /// digest extensions. + /// Digest extension method on the . /// - public static class StreamContentDigestOptionsExtensions + /// + /// of the filestream. + /// No digest is calculated if the stream is not . + /// digest options to use. + /// . + public static StreamContent WithDigest( + this StreamContent content, + Stream fileStream, + FileDigestOptions options) { - /// - /// Digest extension method on the . - /// - /// - /// of the filestream. - /// No digest is calculated if the stream is not . - /// digest options to use. - /// . - public static StreamContent WithDigest( - this StreamContent content, - Stream fileStream, - FileDigestOptions options) - { - if (!options.UseFileDigesting || options.DigestHashAlgorithm == null) { - return content; - } - - SetHashValue(fileStream, options); - - string base64Digest = Convert.ToBase64String(options.DigestHashValue); - - content.Headers.Add("Digest", $"{options.DigestHashAlgorithm}={base64Digest}"); - + if (!options.UseFileDigesting || options.DigestHashAlgorithm == null) { return content; } - private static void SetHashValue( - Stream fileStream, - FileDigestOptions options) - { - if (options.DigestHashValue != null) { - return; - } + SetHashValue(fileStream, options); - if (!fileStream.CanSeek) { - return; - } + string base64Digest = Convert.ToBase64String(options.DigestHashValue); - long position = fileStream.Position; + content.Headers.Add("Digest", $"{options.DigestHashAlgorithm}={base64Digest}"); - using (var algo = HashAlgorithmCreate(options)) { - options.DigestHashValue = algo.ComputeHash(fileStream); - } + return content; + } - fileStream.Position = position; + private static void SetHashValue( + Stream fileStream, + FileDigestOptions options) + { + if (options.DigestHashValue != null) { + return; } - private static HashAlgorithm HashAlgorithmCreate( - FileDigestOptions options) - { - string algorithmName = options.DigestHashAlgorithm; - HashAlgorithm algorithm = null; + if (!fileStream.CanSeek) { + return; + } + + long position = fileStream.Position; + + using (var algo = HashAlgorithmCreate(options)) { + options.DigestHashValue = algo.ComputeHash(fileStream); + } + + fileStream.Position = position; + } + + private static HashAlgorithm HashAlgorithmCreate( + FileDigestOptions options) + { + string algorithmName = options.DigestHashAlgorithm; + HashAlgorithm algorithm = null; #if NET462 - algorithm = HashAlgorithm.Create(algorithmName); + algorithm = HashAlgorithm.Create(algorithmName); #else - algorithm = algorithmName switch { - "SHA1" or "SHA-1" => SHA1.Create(), - "SHA256" or "SHA-256" => SHA256.Create(), - "SHA384" or "SHA-384" => SHA384.Create(), - "SHA512" or "SHA-512" => SHA512.Create(), - - _ => null, - }; + algorithm = algorithmName switch { + "SHA1" or "SHA-1" => SHA1.Create(), + "SHA256" or "SHA-256" => SHA256.Create(), + "SHA384" or "SHA-384" => SHA384.Create(), + "SHA512" or "SHA-512" => SHA512.Create(), + + _ => null, + }; #endif - if (algorithm == null && options.DigestHashValue == null) { - algorithm = DefaultHashAlgorithm(); - options.DigestHashAlgorithm = algorithm.GetType().Name; - } - - if (algorithm == null) { - throw new InvalidOperationException($"No hash algorithm for '{algorithmName}'"); - } + if (algorithm == null && options.DigestHashValue == null) { + algorithm = DefaultHashAlgorithm(); + options.DigestHashAlgorithm = algorithm.GetType().Name; + } - return algorithm; + if (algorithm == null) { + throw new InvalidOperationException($"No hash algorithm for '{algorithmName}'"); } - private static HashAlgorithm DefaultHashAlgorithm() => + return algorithm; + } + + private static HashAlgorithm DefaultHashAlgorithm() => #if NET462 - HashAlgorithm.Create(); + HashAlgorithm.Create(); #else - SHA256.Create(); + SHA256.Create(); #endif - } } diff --git a/src/SignhostAPIClient/Rest/UriPathExtensions.cs b/src/SignhostAPIClient/Rest/UriPathExtensions.cs index 39fb97fb..9f13c529 100644 --- a/src/SignhostAPIClient/Rest/UriPathExtensions.cs +++ b/src/SignhostAPIClient/Rest/UriPathExtensions.cs @@ -1,25 +1,24 @@ using System; using System.Linq; -namespace Signhost.APIClient.Rest +namespace Signhost.APIClient.Rest; + +internal static class UriPathExtensions { - internal static class UriPathExtensions + internal static UriBuilder AppendPathSegment(this string url) { - internal static UriBuilder AppendPathSegment(this string url) - { - var builder = new UriBuilder(url); + var builder = new UriBuilder(url); - return builder; - } + return builder; + } - internal static Uri JoinPaths( - this string url, - params string[] segments) - { - var segmentList = segments.ToList(); - segmentList.Insert(0, url); - var escaped = segmentList.Select(seg => Uri.EscapeDataString(seg)); - return new Uri(string.Join("/", escaped), UriKind.Relative); - } + internal static Uri JoinPaths( + this string url, + params string[] segments) + { + var segmentList = segments.ToList(); + segmentList.Insert(0, url); + var escaped = segmentList.Select(seg => Uri.EscapeDataString(seg)); + return new Uri(string.Join("/", escaped), UriKind.Relative); } } From f289e269c0a86002a1a79e4ad82711ca4199ad8c Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Fri, 26 Sep 2025 19:28:30 +0200 Subject: [PATCH 08/22] Remove support weak digest hash algos Also refactor file digest options to use enum for hash algorithms and update related tests --- .../SignhostApiClientTests.cs | 6 +- .../Rest/DigestHashAlgorithmNames.cs | 30 ++++++++++ .../Rest/FileDigestOptions.cs | 4 +- .../StreamContentDigestOptionsExtensions.cs | 55 ++++++++----------- 4 files changed, 59 insertions(+), 36 deletions(-) create mode 100644 src/SignhostAPIClient/Rest/DigestHashAlgorithmNames.cs diff --git a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs index aee6ccff..80e0fbb7 100644 --- a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs +++ b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs @@ -462,7 +462,7 @@ await signhostApiClient.AddOrReplaceFileToTransactionAsync( new FileUploadOptions{ DigestOptions = new FileDigestOptions { - DigestHashAlgorithm = "SHA-512" + DigestHashAlgorithm = DigestHashAlgorithm.SHA512 } }); } @@ -476,7 +476,7 @@ public async Task when_AddOrReplaceFileToTransaction_with_digest_value_is_used_a var mockHttp = new MockHttpMessageHandler(); mockHttp .Expect(HttpMethod.Put, "http://localhost/api/transaction/transaction Id/file/file Id") - .WithHeaders("Digest", "SHA-1=AAEC") + .WithHeaders("Digest", "SHA-256=AAEC") .Respond(HttpStatusCode.OK); using (var httpClient = mockHttp.ToHttpClient()) { @@ -490,7 +490,7 @@ await signhostApiClient.AddOrReplaceFileToTransactionAsync( { DigestOptions = new FileDigestOptions { - DigestHashAlgorithm = "SHA-1", + DigestHashAlgorithm = DigestHashAlgorithm.SHA256, DigestHashValue = new byte[] { 0x00, 0x01, 0x02 } } }); diff --git a/src/SignhostAPIClient/Rest/DigestHashAlgorithmNames.cs b/src/SignhostAPIClient/Rest/DigestHashAlgorithmNames.cs new file mode 100644 index 00000000..c41d87e9 --- /dev/null +++ b/src/SignhostAPIClient/Rest/DigestHashAlgorithmNames.cs @@ -0,0 +1,30 @@ +namespace Signhost.APIClient.Rest; + +/// +/// Provides constants for hash algorithm names used in HTTP Digest headers, +/// following the naming conventions specified in RFC 3230 (Instance Digests in HTTP) +/// and RFC 5843 (Additional Hash Algorithms for HTTP Instance Digests). +/// +/// These names are in accordance with the Digest header in HTTP requests, +/// where the header specifies the algorithm used to create the digest of the resource. +/// +/// For more information: +/// https://evidos.github.io/endpoints/##/paths//api/transaction/%7BtransactionId%7D/file/%7BfileId%7D/put +/// +public enum DigestHashAlgorithm +{ + /// + /// Use no digest. + /// + None = 0, + + /// + /// SHA-256 hash algorithm, as specified in RFC 5843. + /// + SHA256, + + /// + /// SHA-512 hash algorithm, as specified in RFC 5843. + /// + SHA512, +} diff --git a/src/SignhostAPIClient/Rest/FileDigestOptions.cs b/src/SignhostAPIClient/Rest/FileDigestOptions.cs index bc0218b6..c5580a2a 100644 --- a/src/SignhostAPIClient/Rest/FileDigestOptions.cs +++ b/src/SignhostAPIClient/Rest/FileDigestOptions.cs @@ -1,7 +1,7 @@ namespace Signhost.APIClient.Rest; /// -/// File digest options for file uploads +/// File digest options for file uploads. /// public class FileDigestOptions { @@ -16,7 +16,7 @@ public class FileDigestOptions /// the hash value or the digest algorithm that is used /// to set the . /// - public string DigestHashAlgorithm { get; set; } = "SHA-256"; + public DigestHashAlgorithm DigestHashAlgorithm { get; set; } = DigestHashAlgorithm.SHA256; /// /// Gets or sets the hash digest value, you can set this yourself diff --git a/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs b/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs index eb75441a..32e27d47 100644 --- a/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs +++ b/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs @@ -23,7 +23,10 @@ public static StreamContent WithDigest( Stream fileStream, FileDigestOptions options) { - if (!options.UseFileDigesting || options.DigestHashAlgorithm == null) { + if ( + !options.UseFileDigesting || + options.DigestHashAlgorithm == DigestHashAlgorithm.None + ) { return content; } @@ -31,11 +34,22 @@ public static StreamContent WithDigest( string base64Digest = Convert.ToBase64String(options.DigestHashValue); - content.Headers.Add("Digest", $"{options.DigestHashAlgorithm}={base64Digest}"); + content.Headers.Add("Digest", $"{GetDigestHashAlgorithmName(options)}={base64Digest}"); return content; } + private static string GetDigestHashAlgorithmName(FileDigestOptions options) + { + return options.DigestHashAlgorithm switch { + DigestHashAlgorithm.SHA256 => "SHA-256", + DigestHashAlgorithm.SHA512 => "SHA-512", + + _ => throw new InvalidOperationException( + $"No hash algorithm name for '{options.DigestHashAlgorithm}'"), + }; + } + private static void SetHashValue( Stream fileStream, FileDigestOptions options) @@ -60,37 +74,16 @@ private static void SetHashValue( private static HashAlgorithm HashAlgorithmCreate( FileDigestOptions options) { - string algorithmName = options.DigestHashAlgorithm; - HashAlgorithm algorithm = null; - + return options.DigestHashAlgorithm switch { #if NET462 - algorithm = HashAlgorithm.Create(algorithmName); + DigestHashAlgorithm.SHA256 => HashAlgorithm.Create("SHA256"), + DigestHashAlgorithm.SHA512 => HashAlgorithm.Create("SHA512"), #else - algorithm = algorithmName switch { - "SHA1" or "SHA-1" => SHA1.Create(), - "SHA256" or "SHA-256" => SHA256.Create(), - "SHA384" or "SHA-384" => SHA384.Create(), - "SHA512" or "SHA-512" => SHA512.Create(), - - _ => null, - }; + DigestHashAlgorithm.SHA256 => SHA256.Create(), + DigestHashAlgorithm.SHA512 => SHA512.Create(), #endif - if (algorithm == null && options.DigestHashValue == null) { - algorithm = DefaultHashAlgorithm(); - options.DigestHashAlgorithm = algorithm.GetType().Name; - } - - if (algorithm == null) { - throw new InvalidOperationException($"No hash algorithm for '{algorithmName}'"); - } - - return algorithm; + _ => throw new InvalidOperationException( + $"No hash algorithm for '{options.DigestHashAlgorithm}'"), + }; } - - private static HashAlgorithm DefaultHashAlgorithm() => -#if NET462 - HashAlgorithm.Create(); -#else - SHA256.Create(); -#endif } From 97670479ef1216ea0ddc9df060050d1e0984f4a4 Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Thu, 27 Nov 2025 21:14:08 +0100 Subject: [PATCH 09/22] Make remove documentation warnings --- src/signhost.ruleset | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/signhost.ruleset b/src/signhost.ruleset index 51c9a98e..d2259345 100644 --- a/src/signhost.ruleset +++ b/src/signhost.ruleset @@ -9,9 +9,16 @@ + + + + + + + From 74c46ffafe8a5876b23a3e9c1d271e736dac03f8 Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Thu, 27 Nov 2025 21:14:42 +0100 Subject: [PATCH 10/22] Refactor to nullable enable --- .../TransactionTests.cs | 1 - src/SignhostAPIClient.Tests/PostbackTests.cs | 2 - src/SignhostAPIClient/NotNullWhenAttribute.cs | 18 +++++ src/SignhostAPIClient/Rest/ApiResponse.cs | 6 +- .../Rest/DataObjects/Activity.cs | 6 +- .../DataObjects/DeleteTransactionOptions.cs | 2 +- .../Rest/DataObjects/FileEntry.cs | 4 +- .../Rest/DataObjects/FileLink.cs | 6 +- .../DataObjects/{ => FileMetaData}/Field.cs | 6 +- .../{ => FileMetaData}/FileMeta.cs | 10 +-- .../{ => FileMetaData}/FileSignerMeta.cs | 2 +- .../{ => FileMetaData}/Location.cs | 2 +- .../Rest/DataObjects/IdealVerification.cs | 11 ---- .../Rest/DataObjects/IdinVerification.cs | 18 ----- .../Rest/DataObjects/PostbackTransaction.cs | 2 +- .../Rest/DataObjects/Receiver.cs | 30 +++------ .../Rest/DataObjects/Signer.cs | 59 ++++++----------- .../Rest/DataObjects/Transaction.cs | 16 ++--- .../ConsentVerification.cs | 0 .../{ => Verifications}/CscVerification.cs | 10 +-- .../{ => Verifications}/DigidVerification.cs | 4 +- .../EherkenningVerification.cs | 4 +- .../EidasLoginVerification.cs | 8 +-- .../IPAddressVerification.cs | 2 +- .../{ => Verifications}/IVerification.cs | 0 .../Verifications/IdealVerification.cs | 11 ++++ .../Verifications/IdinVerification.cs | 17 +++++ .../ItsmeIdentificationVerification.cs | 6 +- .../DataObjects/{ => Verifications}/Level.cs | 2 +- .../{ => Verifications}/OidcVerification.cs | 2 +- .../{ => Verifications}/OnfidoVerification.cs | 0 .../PhoneNumberVerification.cs | 2 +- .../ScribbleVerification.cs | 2 +- .../SurfnetVerification.cs | 4 +- .../Rest/ErrorHandling/GoneException.cs | 4 +- ...pResponseMessageErrorHandlingExtensions.cs | 53 +++++---------- .../InternalServerErrorException.cs | 10 +-- .../SignhostRestApiClientException.cs | 2 +- .../Rest/FileDigestOptions.cs | 2 +- .../Rest/HttpContentJsonExtensions.cs | 6 +- .../Rest/ISignHostApiClient.cs | 2 +- .../Rest/ISignhostApiClientSettings.cs | 4 +- .../Rest/ISignhostApiReceiver.cs | 3 +- .../Rest/SignHostApiClient.cs | 65 ++++++++++--------- .../Rest/SignHostApiClientSettings.cs | 4 +- .../Rest/SignhostApiReceiver.cs | 22 ++++--- .../StreamContentDigestOptionsExtensions.cs | 6 +- .../SignhostAPIClient.csproj | 1 + 48 files changed, 225 insertions(+), 234 deletions(-) create mode 100644 src/SignhostAPIClient/NotNullWhenAttribute.cs rename src/SignhostAPIClient/Rest/DataObjects/{ => FileMetaData}/Field.cs (55%) rename src/SignhostAPIClient/Rest/DataObjects/{ => FileMetaData}/FileMeta.cs (76%) rename src/SignhostAPIClient/Rest/DataObjects/{ => FileMetaData}/FileSignerMeta.cs (61%) rename src/SignhostAPIClient/Rest/DataObjects/{ => FileMetaData}/Location.cs (90%) delete mode 100644 src/SignhostAPIClient/Rest/DataObjects/IdealVerification.cs delete mode 100644 src/SignhostAPIClient/Rest/DataObjects/IdinVerification.cs rename src/SignhostAPIClient/Rest/DataObjects/{ => Verifications}/ConsentVerification.cs (100%) rename src/SignhostAPIClient/Rest/DataObjects/{ => Verifications}/CscVerification.cs (72%) rename src/SignhostAPIClient/Rest/DataObjects/{ => Verifications}/DigidVerification.cs (62%) rename src/SignhostAPIClient/Rest/DataObjects/{ => Verifications}/EherkenningVerification.cs (74%) rename src/SignhostAPIClient/Rest/DataObjects/{ => Verifications}/EidasLoginVerification.cs (82%) rename src/SignhostAPIClient/Rest/DataObjects/{ => Verifications}/IPAddressVerification.cs (87%) rename src/SignhostAPIClient/Rest/DataObjects/{ => Verifications}/IVerification.cs (100%) create mode 100644 src/SignhostAPIClient/Rest/DataObjects/Verifications/IdealVerification.cs create mode 100644 src/SignhostAPIClient/Rest/DataObjects/Verifications/IdinVerification.cs rename src/SignhostAPIClient/Rest/DataObjects/{ => Verifications}/ItsmeIdentificationVerification.cs (63%) rename src/SignhostAPIClient/Rest/DataObjects/{ => Verifications}/Level.cs (86%) rename src/SignhostAPIClient/Rest/DataObjects/{ => Verifications}/OidcVerification.cs (84%) rename src/SignhostAPIClient/Rest/DataObjects/{ => Verifications}/OnfidoVerification.cs (100%) rename src/SignhostAPIClient/Rest/DataObjects/{ => Verifications}/PhoneNumberVerification.cs (76%) rename src/SignhostAPIClient/Rest/DataObjects/{ => Verifications}/ScribbleVerification.cs (82%) rename src/SignhostAPIClient/Rest/DataObjects/{ => Verifications}/SurfnetVerification.cs (59%) diff --git a/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs b/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs index 0e3f6303..3bcfb807 100644 --- a/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs +++ b/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs @@ -121,7 +121,6 @@ public async Task Given_complex_transaction_When_created_and_started_Then_all_pr createdTransaction.SendEmailNotifications.Should().BeFalse(); createdTransaction.SignRequestMode.Should().Be(2); createdTransaction.Language.Should().Be("en-US"); - createdTransaction.CreatedDateTime.Should().HaveValue(); createdTransaction.CreatedDateTime.Should().BeCloseTo(DateTimeOffset.UtcNow, TimeSpan.FromMinutes(1)); createdTransaction.CanceledDateTime.Should().BeNull(); createdTransaction.CancellationReason.Should().BeNull(); diff --git a/src/SignhostAPIClient.Tests/PostbackTests.cs b/src/SignhostAPIClient.Tests/PostbackTests.cs index 8d3314cd..c469638f 100644 --- a/src/SignhostAPIClient.Tests/PostbackTests.cs +++ b/src/SignhostAPIClient.Tests/PostbackTests.cs @@ -41,8 +41,6 @@ public void PostbackTransaction_should_get_serialized_correctly() signer.SignRequestMessage .Should().Be("Hello, could you please sign this document? Best regards, John Doe"); signer.DaysToRemind .Should().Be(15); signer.Language .Should().Be("en-US"); - signer.ScribbleName .Should().Be("John Doe"); - signer.ScribbleNameFixed .Should().BeFalse(); signer.Reference .Should().Be("Client #123"); signer.ReturnUrl .Should().Be("https://signhost.com"); signer.RejectReason .Should().BeNull(); diff --git a/src/SignhostAPIClient/NotNullWhenAttribute.cs b/src/SignhostAPIClient/NotNullWhenAttribute.cs new file mode 100644 index 00000000..c222fdbf --- /dev/null +++ b/src/SignhostAPIClient/NotNullWhenAttribute.cs @@ -0,0 +1,18 @@ +#if NETFRAMEWORK || NETSTANDARD2_0 +using System; + +namespace Signhost.APIClient.Rest; + +public sealed class NotNullWhenAttribute + : Attribute +{ + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } +} +#endif diff --git a/src/SignhostAPIClient/Rest/ApiResponse.cs b/src/SignhostAPIClient/Rest/ApiResponse.cs index bdd84e37..27a6234f 100644 --- a/src/SignhostAPIClient/Rest/ApiResponse.cs +++ b/src/SignhostAPIClient/Rest/ApiResponse.cs @@ -7,13 +7,13 @@ public class ApiResponse { private readonly HttpResponseMessage httpResponse; - public ApiResponse(HttpResponseMessage httpResponse, TValue value) + public ApiResponse(HttpResponseMessage httpResponse, TValue? value) { this.httpResponse = httpResponse; this.Value = value; } - public TValue Value { get; private set; } + public TValue? Value { get; private set; } public HttpStatusCode HttpStatusCode => httpResponse.StatusCode; @@ -21,7 +21,7 @@ public void EnsureAvailableStatusCode() { if (HttpStatusCode == HttpStatusCode.Gone) { throw new ErrorHandling.GoneException( - httpResponse.ReasonPhrase, + httpResponse.ReasonPhrase ?? "No reason phrase provided", Value) { // TO-DO: Make async in v5 diff --git a/src/SignhostAPIClient/Rest/DataObjects/Activity.cs b/src/SignhostAPIClient/Rest/DataObjects/Activity.cs index 37a9811d..c550a2b0 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Activity.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Activity.cs @@ -5,14 +5,14 @@ namespace Signhost.APIClient.Rest.DataObjects; public class Activity { - public string Id { get; set; } + public string Id { get; set; } = default!; public ActivityType Code { get; set; } [JsonPropertyName("Activity")] - public string ActivityValue { get; set; } + public string? ActivityValue { get; set; } - public string Info { get; set; } + public string? Info { get; set; } public DateTimeOffset CreatedDateTime { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/DeleteTransactionOptions.cs b/src/SignhostAPIClient/Rest/DataObjects/DeleteTransactionOptions.cs index ae3ecc5a..5defee9e 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/DeleteTransactionOptions.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/DeleteTransactionOptions.cs @@ -11,5 +11,5 @@ public class DeleteTransactionOptions /// /// Gets or sets the reason of cancellation. /// - public string Reason { get; set; } + public string? Reason { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileEntry.cs b/src/SignhostAPIClient/Rest/DataObjects/FileEntry.cs index f1565e5e..208c0708 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileEntry.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileEntry.cs @@ -4,7 +4,7 @@ namespace Signhost.APIClient.Rest.DataObjects; public class FileEntry { - public IList Links { get; set; } + public IList Links { get; set; } = default!; - public string DisplayName { get; set; } + public string? DisplayName { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileLink.cs b/src/SignhostAPIClient/Rest/DataObjects/FileLink.cs index f503d5ae..6fe1ad17 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileLink.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileLink.cs @@ -2,9 +2,9 @@ public class FileLink { - public string Rel { get; set; } + public string Rel { get; set; } = default!; - public string Type { get; set; } + public string Type { get; set; } = default!; - public string Link { get; set; } + public string Link { get; set; } = default!; } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Field.cs b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Field.cs similarity index 55% rename from src/SignhostAPIClient/Rest/DataObjects/Field.cs rename to src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Field.cs index 58cd3f9d..62f40b0a 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Field.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Field.cs @@ -3,10 +3,10 @@ public class Field { // TO-DO: Make enum in v5. - public string Type { get; set; } + public string Type { get; set; } = default!; // TO-DO: Can be boolean, number, string, should be fixed in v5. - public string Value { get; set; } + public string? Value { get; set; } - public Location Location { get; set; } + public Location Location { get; set; } = default!; } diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileMeta.cs b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileMeta.cs similarity index 76% rename from src/SignhostAPIClient/Rest/DataObjects/FileMeta.cs rename to src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileMeta.cs index b20b72e5..93ab26c1 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileMeta.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileMeta.cs @@ -7,13 +7,15 @@ public class FileMeta { public int? DisplayOrder { get; set; } - public string DisplayName { get; set; } + public string? DisplayName { get; set; } - public string Description { get; set; } + public string? Description { get; set; } - public IDictionary Signers { get; set; } + public IDictionary Signers { get; set; } = + new Dictionary(); - public IDictionary> FormSets { get; set; } + public IDictionary> FormSets { get; set; } = + new Dictionary>(); /// /// Gets or sets whether to use the scribble signature as a paraph diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileSignerMeta.cs b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileSignerMeta.cs similarity index 61% rename from src/SignhostAPIClient/Rest/DataObjects/FileSignerMeta.cs rename to src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileSignerMeta.cs index 7adfc404..6ef1c7ff 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileSignerMeta.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileSignerMeta.cs @@ -2,5 +2,5 @@ public class FileSignerMeta { - public string[] FormSets { get; set; } + public string[] FormSets { get; set; } = default!; } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Location.cs b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Location.cs similarity index 90% rename from src/SignhostAPIClient/Rest/DataObjects/Location.cs rename to src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Location.cs index 1799ad6b..44926362 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Location.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Location.cs @@ -2,7 +2,7 @@ public class Location { - public string Search { get; set; } + public string? Search { get; set; } public int? Occurence { get; set; } diff --git a/src/SignhostAPIClient/Rest/DataObjects/IdealVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/IdealVerification.cs deleted file mode 100644 index 55f1bc95..00000000 --- a/src/SignhostAPIClient/Rest/DataObjects/IdealVerification.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Signhost.APIClient.Rest.DataObjects; - -public class IdealVerification - : IVerification -{ - public string Iban { get; set; } - - public string AccountHolderName { get; set; } - - public string AccountHolderCity { get; set; } -} diff --git a/src/SignhostAPIClient/Rest/DataObjects/IdinVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/IdinVerification.cs deleted file mode 100644 index 150f398b..00000000 --- a/src/SignhostAPIClient/Rest/DataObjects/IdinVerification.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Signhost.APIClient.Rest.DataObjects; - -public class IdinVerification - : IVerification -{ - public string AccountHolderName { get; set; } - - public string AccountHolderAddress1 { get; set; } - - public string AccountHolderAddress2 { get; set; } - - public DateTime AccountHolderDateOfBirth { get; set; } - - public IDictionary Attributes { get; set; } -} diff --git a/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs b/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs index cffc6ebb..5ae74e50 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs @@ -6,5 +6,5 @@ public class PostbackTransaction : Transaction { [JsonPropertyName("Checksum")] - public string Checksum { get; set; } + public string Checksum { get; set; } = default!; } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Receiver.cs b/src/SignhostAPIClient/Rest/DataObjects/Receiver.cs index b044b37f..32937d69 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Receiver.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Receiver.cs @@ -1,41 +1,29 @@ using System; using System.Collections.Generic; -using System.Text.Json.Serialization; namespace Signhost.APIClient.Rest.DataObjects; public class Receiver { - public Receiver() - { - } + public string? Id { get; set; } - [JsonConstructor] - private Receiver(IReadOnlyList activities) - { - Activities = activities; - } + public string? Name { get; set; } - public string Id { get; set; } + public string? Email { get; set; } - public string Name { get; set; } + public string? Language { get; set; } - public string Email { get; set; } + public string? Subject { get; set; } - public string Language { get; set; } + public string? Message { get; set; } - public string Subject { get; set; } - - public string Message { get; set; } - - public string Reference { get; set; } + public string? Reference { get; set; } public DateTimeOffset? CreatedDateTime { get; set; } public DateTimeOffset? ModifiedDateTime { get; set; } - public IReadOnlyList Activities { get; set; } = - new List().AsReadOnly(); + public IList? Activities { get; set; } - public dynamic Context { get; set; } + public dynamic? Context { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Signer.cs b/src/SignhostAPIClient/Rest/DataObjects/Signer.cs index 944c2571..9b321828 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Signer.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Signer.cs @@ -1,38 +1,25 @@ using System; using System.Collections.Generic; -using System.Text.Json.Serialization; namespace Signhost.APIClient.Rest.DataObjects; public class Signer { - public Signer() - { - } - - [JsonConstructor] - private Signer(IReadOnlyList activities) - { - Activities = activities; - } - - public string Id { get; set; } + public string? Id { get; set; } public DateTimeOffset? Expires { get; set; } - public string Email { get; set; } + public string Email { get; set; } = default!; - public string IntroText { get; set; } + public string? IntroText { get; set; } - public string SignRequestSubject { get; set; } + public string? SignRequestSubject { get; set; } - public string SignRequestMessage { get; set; } + public string? SignRequestMessage { get; set; } - public IList Authentications { get; set; } - = new List(); + public IList Authentications { get; set; } = default!; - public IList Verifications { get; set; } - = new List(); + public IList Verifications { get; set; } = default!; public bool SendSignRequest { get; set; } @@ -40,29 +27,25 @@ private Signer(IReadOnlyList activities) public int? DaysToRemind { get; set; } - public string Language { get; set; } - - public string ScribbleName { get; set; } - - public bool ScribbleNameFixed { get; set; } + public string? Language { get; set; } - public string Reference { get; set; } + public string? Reference { get; set; } - public string ReturnUrl { get; set; } + public string? ReturnUrl { get; set; } - public string RejectReason { get; set; } + public string? RejectReason { get; set; } - public string SignUrl { get; set; } + public string SignUrl { get; set; } = default!; public bool AllowDelegation { get; set; } - public string DelegateSignUrl { get; set; } + public string? DelegateSignUrl { get; set; } - public string DelegateReason { get; set; } + public string? DelegateReason { get; set; } - public string DelegateSignerEmail { get; set; } + public string? DelegateSignerEmail { get; set; } - public string DelegateSignerName { get; set; } + public string? DelegateSignerName { get; set; } public DateTimeOffset? SignedDateTime { get; set; } @@ -74,12 +57,12 @@ private Signer(IReadOnlyList activities) public DateTimeOffset? ModifiedDateTime { get; set; } - public string ShowUrl { get; set; } + public string ShowUrl { get; set; } = default!; - public string ReceiptUrl { get; set; } + public string ReceiptUrl { get; set; } = default!; - public IReadOnlyList Activities { get; private set; } = - new List().AsReadOnly(); + public IList Activities { get; set; } = + new List(); - public dynamic Context { get; set; } + public dynamic? Context { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs b/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs index 7ac62205..e3953701 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs @@ -5,12 +5,12 @@ namespace Signhost.APIClient.Rest.DataObjects; public class Transaction { - public string Id { get; set; } + public string Id { get; set; } = default!; /// /// Gets the when the was created. /// - public DateTimeOffset? CreatedDateTime { get; set; } + public DateTimeOffset CreatedDateTime { get; set; } /// /// Gets the when the was cancelled. @@ -21,9 +21,9 @@ public class Transaction /// /// Gets the cancellation reason when the was cancelled. /// - public string CancellationReason { get; set; } + public string? CancellationReason { get; set; } - public IReadOnlyDictionary Files { get; set; } = + public IDictionary Files { get; set; } = new Dictionary(); public TransactionStatus Status { get; set; } @@ -36,17 +36,17 @@ public class Transaction public IList Receivers { get; set; } = new List(); - public string Reference { get; set; } + public string? Reference { get; set; } - public string PostbackUrl { get; set; } + public string? PostbackUrl { get; set; } public int SignRequestMode { get; set; } public int DaysToExpire { get; set; } - public string Language { get; set; } + public string? Language { get; set; } public bool SendEmailNotifications { get; set; } - public dynamic Context { get; set; } + public dynamic? Context { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/ConsentVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/ConsentVerification.cs similarity index 100% rename from src/SignhostAPIClient/Rest/DataObjects/ConsentVerification.cs rename to src/SignhostAPIClient/Rest/DataObjects/Verifications/ConsentVerification.cs diff --git a/src/SignhostAPIClient/Rest/DataObjects/CscVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/CscVerification.cs similarity index 72% rename from src/SignhostAPIClient/Rest/DataObjects/CscVerification.cs rename to src/SignhostAPIClient/Rest/DataObjects/Verifications/CscVerification.cs index 3befa8de..6a17c85d 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/CscVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/CscVerification.cs @@ -11,25 +11,25 @@ public class CscVerification /// /// Gets or sets the provider identifier. /// - public string Provider { get; set; } + public string? Provider { get; set; } /// /// Gets or sets the certificate issuer. /// - public string Issuer { get; set; } + public string? Issuer { get; set; } /// /// Gets or sets the certificate subject. /// - public string Subject { get; set; } + public string? Subject { get; set; } /// /// Gets or sets the certificate thumbprint. /// - public string Thumbprint { get; set; } + public string? Thumbprint { get; set; } /// /// Gets or sets additional user data. /// - public Dictionary AdditionalUserData { get; set; } + public Dictionary? AdditionalUserData { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/DigidVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/DigidVerification.cs similarity index 62% rename from src/SignhostAPIClient/Rest/DataObjects/DigidVerification.cs rename to src/SignhostAPIClient/Rest/DataObjects/Verifications/DigidVerification.cs index 6ec39b90..d0bb4053 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/DigidVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/DigidVerification.cs @@ -3,7 +3,9 @@ public class DigidVerification : IVerification { - public string Bsn { get; set; } + public string? Bsn { get; set; } + + public string? Betrouwbaarheidsniveau { get; set; } public bool? SecureDownload { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/EherkenningVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/EherkenningVerification.cs similarity index 74% rename from src/SignhostAPIClient/Rest/DataObjects/EherkenningVerification.cs rename to src/SignhostAPIClient/Rest/DataObjects/Verifications/EherkenningVerification.cs index 838144b6..b5799217 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/EherkenningVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/EherkenningVerification.cs @@ -6,10 +6,10 @@ public class EherkenningVerification /// /// Gets or sets the Uid. /// - public string Uid { get; set; } + public string? Uid { get; set; } /// /// Gets or sets the entity concern ID / KVK number. /// - public string EntityConcernIdKvkNr { get; set; } + public string? EntityConcernIdKvkNr { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/EidasLoginVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/EidasLoginVerification.cs similarity index 82% rename from src/SignhostAPIClient/Rest/DataObjects/EidasLoginVerification.cs rename to src/SignhostAPIClient/Rest/DataObjects/Verifications/EidasLoginVerification.cs index 3d54b0ef..13dd4d5e 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/EidasLoginVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/EidasLoginVerification.cs @@ -14,7 +14,7 @@ public class EidasLoginVerification /// /// Gets or sets the uid. /// - public string Uid { get; set; } + public string? Uid { get; set; } /// /// Gets or sets the level. @@ -25,12 +25,12 @@ public class EidasLoginVerification /// /// Gets or sets the first name. /// - public string FirstName { get; set; } + public string? FirstName { get; set; } /// /// Gets or sets the last name. /// - public string LastName { get; set; } + public string? LastName { get; set; } /// /// Gets or sets the date of birth. @@ -40,5 +40,5 @@ public class EidasLoginVerification /// /// Gets or sets the eIDAS attributes. /// - public IDictionary Attributes { get; set; } + public IDictionary? Attributes { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/IPAddressVerification.cs similarity index 87% rename from src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs rename to src/SignhostAPIClient/Rest/DataObjects/Verifications/IPAddressVerification.cs index b1fb67f3..f3141b4b 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/IPAddressVerification.cs @@ -9,5 +9,5 @@ public class IPAddressVerification /// /// Gets or sets the IP Address used by the signer while signing the documents. /// - public string IPAddress { get; set; } + public string? IPAddress { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/IVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/IVerification.cs similarity index 100% rename from src/SignhostAPIClient/Rest/DataObjects/IVerification.cs rename to src/SignhostAPIClient/Rest/DataObjects/Verifications/IVerification.cs diff --git a/src/SignhostAPIClient/Rest/DataObjects/Verifications/IdealVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/IdealVerification.cs new file mode 100644 index 00000000..53600a4a --- /dev/null +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/IdealVerification.cs @@ -0,0 +1,11 @@ +namespace Signhost.APIClient.Rest.DataObjects; + +public class IdealVerification + : IVerification +{ + public string Iban { get; set; } = default!; + + public string? AccountHolderName { get; set; } + + public string? AccountHolderCity { get; set; } +} diff --git a/src/SignhostAPIClient/Rest/DataObjects/Verifications/IdinVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/IdinVerification.cs new file mode 100644 index 00000000..f753810c --- /dev/null +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/IdinVerification.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Signhost.APIClient.Rest.DataObjects; + +public class IdinVerification + : IVerification +{ + public string? AccountHolderName { get; set; } + + public string? AccountHolderAddress1 { get; set; } + + public string? AccountHolderAddress2 { get; set; } + + public string? AccountHolderDateOfBirth { get; set; } + + public IDictionary? Attributes { get; set; } +} diff --git a/src/SignhostAPIClient/Rest/DataObjects/ItsmeIdentificationVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/ItsmeIdentificationVerification.cs similarity index 63% rename from src/SignhostAPIClient/Rest/DataObjects/ItsmeIdentificationVerification.cs rename to src/SignhostAPIClient/Rest/DataObjects/Verifications/ItsmeIdentificationVerification.cs index 914665e4..76bd5821 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ItsmeIdentificationVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/ItsmeIdentificationVerification.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace Signhost.APIClient.Rest.DataObjects; /// @@ -9,5 +11,7 @@ public class ItsmeIdentificationVerification /// /// Gets or sets the phonenumber. /// - public string PhoneNumber { get; set; } + public string PhoneNumber { get; set; } = default!; + + public IDictionary? Attributes { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Level.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/Level.cs similarity index 86% rename from src/SignhostAPIClient/Rest/DataObjects/Level.cs rename to src/SignhostAPIClient/Rest/DataObjects/Verifications/Level.cs index a96cd7f5..cf4dcd9e 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Level.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/Level.cs @@ -1,7 +1,7 @@ namespace Signhost.APIClient.Rest.DataObjects; /// -/// Level of Assurance. +/// Level of Assurance of eIDAS Login verification. /// public enum Level { diff --git a/src/SignhostAPIClient/Rest/DataObjects/OidcVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/OidcVerification.cs similarity index 84% rename from src/SignhostAPIClient/Rest/DataObjects/OidcVerification.cs rename to src/SignhostAPIClient/Rest/DataObjects/Verifications/OidcVerification.cs index 608f9dff..eea7aece 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/OidcVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/OidcVerification.cs @@ -9,5 +9,5 @@ public class OidcVerification /// /// Gets or sets the OIDC provider name. /// - public string ProviderName { get; set; } + public string? ProviderName { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/OnfidoVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/OnfidoVerification.cs similarity index 100% rename from src/SignhostAPIClient/Rest/DataObjects/OnfidoVerification.cs rename to src/SignhostAPIClient/Rest/DataObjects/Verifications/OnfidoVerification.cs diff --git a/src/SignhostAPIClient/Rest/DataObjects/PhoneNumberVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/PhoneNumberVerification.cs similarity index 76% rename from src/SignhostAPIClient/Rest/DataObjects/PhoneNumberVerification.cs rename to src/SignhostAPIClient/Rest/DataObjects/Verifications/PhoneNumberVerification.cs index 38640a10..b1c6867c 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/PhoneNumberVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/PhoneNumberVerification.cs @@ -3,7 +3,7 @@ public class PhoneNumberVerification : IVerification { - public string Number { get; set; } + public string Number { get; set; } = default!; public bool? SecureDownload { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/ScribbleVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/ScribbleVerification.cs similarity index 82% rename from src/SignhostAPIClient/Rest/DataObjects/ScribbleVerification.cs rename to src/SignhostAPIClient/Rest/DataObjects/Verifications/ScribbleVerification.cs index 5556f0b5..4cb1ee25 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ScribbleVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/ScribbleVerification.cs @@ -7,5 +7,5 @@ public class ScribbleVerification public bool ScribbleNameFixed { get; set; } - public string ScribbleName { get; set; } + public string? ScribbleName { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/SurfnetVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/SurfnetVerification.cs similarity index 59% rename from src/SignhostAPIClient/Rest/DataObjects/SurfnetVerification.cs rename to src/SignhostAPIClient/Rest/DataObjects/Verifications/SurfnetVerification.cs index 8523e733..a34adc46 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/SurfnetVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/SurfnetVerification.cs @@ -5,7 +5,7 @@ namespace Signhost.APIClient.Rest.DataObjects; public class SurfnetVerification : IVerification { - public string Uid { get; set; } + public string? Uid { get; set; } - public IDictionary Attributes { get; set; } + public IDictionary? Attributes { get; set; } } diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/GoneException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/GoneException.cs index 5aad3757..077a34dc 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/GoneException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/GoneException.cs @@ -26,7 +26,7 @@ public GoneException(string message) { } - public GoneException(string message, TResult result) + public GoneException(string message, TResult? result) : base(message) { Result = result; @@ -57,5 +57,5 @@ protected GoneException( /// Please note that this no longer contains the full transaction /// details as most data is removed. /// - public TResult Result { get; private set; } + public TResult? Result { get; private set; } } diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs b/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs index 21e6b51d..d43b951f 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs @@ -61,7 +61,7 @@ public static async Task EnsureSignhostSuccessStatusCodeAsy string errorMessage = "Unknown Signhost error"; string responseBody = string.Empty; - if (response.Content != null) { + if (response.Content is not null) { responseBody = await response.Content.ReadAsStringAsync() .ConfigureAwait(false); @@ -72,39 +72,20 @@ public static async Task EnsureSignhostSuccessStatusCodeAsy } // TO-DO: Use switch pattern in v5 - Exception exception = null; - switch (response.StatusCode) { - case HttpStatusCode.Unauthorized: - exception = new UnauthorizedAccessException( - errorMessage); - break; - - case HttpStatusCode.BadRequest: - exception = new BadRequestException( - errorMessage); - break; - - case HttpStatusCode.PaymentRequired - when errorType == OutOfCreditsApiProblemType: - exception = new OutOfCreditsException( - errorMessage); - break; - - case HttpStatusCode.NotFound: - exception = new NotFoundException( - errorMessage); - break; - - case HttpStatusCode.InternalServerError: - exception = new InternalServerErrorException( - errorMessage, response.Headers.RetryAfter); - break; - - default: - exception = new SignhostRestApiClientException( - errorMessage); - break; - } + Exception exception = response.StatusCode switch { + HttpStatusCode.Unauthorized => new UnauthorizedAccessException(errorMessage), + HttpStatusCode.BadRequest => new BadRequestException(errorMessage), + HttpStatusCode.NotFound => new NotFoundException(errorMessage), + + HttpStatusCode.InternalServerError => + new InternalServerErrorException(errorMessage, response.Headers.RetryAfter), + + HttpStatusCode.PaymentRequired + when errorType == OutOfCreditsApiProblemType => + new OutOfCreditsException(errorMessage), + + _ => new SignhostRestApiClientException(errorMessage), + }; if (exception is SignhostRestApiClientException signhostException) { signhostException.ResponseBody = responseBody; @@ -116,9 +97,9 @@ public static async Task EnsureSignhostSuccessStatusCodeAsy private class ErrorResponse { [JsonPropertyName("type")] - public string Type { get; set; } + public string Type { get; set; } = string.Empty; [JsonPropertyName("message")] - public string Message { get; set; } + public string Message { get; set; } = string.Empty; } } diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/InternalServerErrorException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/InternalServerErrorException.cs index 265489f0..ff54e52d 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/InternalServerErrorException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/InternalServerErrorException.cs @@ -18,18 +18,18 @@ public InternalServerErrorException(string message) } public InternalServerErrorException( - string message, RetryConditionHeaderValue retryAfter) + string message, RetryConditionHeaderValue? retryAfter) : base(message) { HelpLink = "https://api.signhost.com/Help"; - if (retryAfter != null) { - if (retryAfter.Date != null) { + if (retryAfter is not null) { + if (retryAfter.Date is not null) { RetryAfter = retryAfter.Date; } - if (retryAfter.Delta != null) { - RetryAfter = DateTime.Now + retryAfter.Delta; + if (retryAfter.Delta is not null) { + RetryAfter = DateTimeOffset.Now + retryAfter.Delta; } } } diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs index 6e30ba82..d7e12967 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs @@ -36,5 +36,5 @@ protected SignhostRestApiClientException( /// /// Gets or sets the response body returned from the Signhost REST API. /// - public string ResponseBody { get; set; } + public string? ResponseBody { get; set; } } diff --git a/src/SignhostAPIClient/Rest/FileDigestOptions.cs b/src/SignhostAPIClient/Rest/FileDigestOptions.cs index c5580a2a..bb5d770f 100644 --- a/src/SignhostAPIClient/Rest/FileDigestOptions.cs +++ b/src/SignhostAPIClient/Rest/FileDigestOptions.cs @@ -22,5 +22,5 @@ public class FileDigestOptions /// Gets or sets the hash digest value, you can set this yourself /// if you know the digest value in advance. /// - public byte[] DigestHashValue { get; set; } + public byte[]? DigestHashValue { get; set; } } diff --git a/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs b/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs index 3dfa535f..f8759986 100644 --- a/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs +++ b/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs @@ -16,11 +16,11 @@ internal static class HttpContentJsonExtensions /// to read. /// A deserialized value of /// or default(T) if no content is available. - internal static async Task FromJsonAsync( + internal static async Task FromJsonAsync( this HttpContent httpContent) { - if (httpContent == null) { - return default(T); + if (httpContent is null) { + return default; } var json = await httpContent.ReadAsStringAsync() diff --git a/src/SignhostAPIClient/Rest/ISignHostApiClient.cs b/src/SignhostAPIClient/Rest/ISignHostApiClient.cs index d2ac880c..ee2fa040 100644 --- a/src/SignhostAPIClient/Rest/ISignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/ISignHostApiClient.cs @@ -207,7 +207,7 @@ Task DeleteTransactionAsync( /// A Task. Task DeleteTransactionAsync( string transactionId, - DeleteTransactionOptions options = default, + DeleteTransactionOptions? options = default, CancellationToken cancellationToken = default); /// diff --git a/src/SignhostAPIClient/Rest/ISignhostApiClientSettings.cs b/src/SignhostAPIClient/Rest/ISignhostApiClientSettings.cs index 22baf84a..f017de58 100644 --- a/src/SignhostAPIClient/Rest/ISignhostApiClientSettings.cs +++ b/src/SignhostAPIClient/Rest/ISignhostApiClientSettings.cs @@ -9,7 +9,7 @@ public interface ISignhostApiClientSettings /// /// Gets the usertoken identifying an authorized user. /// - string UserToken { get; } + string? UserToken { get; } /// /// Gets the app key of your applications. @@ -21,5 +21,5 @@ public interface ISignhostApiClientSettings /// string Endpoint { get; } - Action AddHeader { get; } + Action? AddHeader { get; } } diff --git a/src/SignhostAPIClient/Rest/ISignhostApiReceiver.cs b/src/SignhostAPIClient/Rest/ISignhostApiReceiver.cs index ba4eb961..8b297523 100644 --- a/src/SignhostAPIClient/Rest/ISignhostApiReceiver.cs +++ b/src/SignhostAPIClient/Rest/ISignhostApiReceiver.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Signhost.APIClient.Rest.DataObjects; namespace Signhost.APIClient.Rest; @@ -18,5 +19,5 @@ public interface ISignhostApiReceiver bool IsPostbackChecksumValid( IDictionary headers, string body, - out Transaction postbackTransaction); + [NotNullWhen(true)] out Transaction? postbackTransaction); } diff --git a/src/SignhostAPIClient/Rest/SignHostApiClient.cs b/src/SignhostAPIClient/Rest/SignHostApiClient.cs index 4c7f698b..09adc32e 100644 --- a/src/SignhostAPIClient/Rest/SignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/SignHostApiClient.cs @@ -21,12 +21,8 @@ public class SignhostApiClient { private const string ApiVersion = "v1"; - private static readonly string Version = typeof(SignhostApiClient) -#if TYPEINFO - .GetTypeInfo() -#endif - .Assembly.GetCustomAttribute() - .Version; + private static readonly string Version = GetVersion() + ?? throw new InvalidOperationException("Unknown assembly version."); private readonly ISignhostApiClientSettings settings; private readonly HttpClient client; @@ -86,7 +82,7 @@ public async Task CreateTransactionAsync( Transaction transaction, CancellationToken cancellationToken = default) { - if (transaction == null) { + if (transaction is null) { throw new ArgumentNullException(nameof(transaction)); } @@ -99,7 +95,9 @@ public async Task CreateTransactionAsync( .ConfigureAwait(false); return await result.Content.FromJsonAsync() - .ConfigureAwait(false); + .ConfigureAwait(false) + ?? throw new InvalidOperationException( + "Failed to deserialize the transaction."); } /// @@ -113,7 +111,7 @@ public async Task> GetTransactionResponseAsync( string transactionId, CancellationToken cancellationToken = default) { - if (transactionId == null) { + if (transactionId is null) { throw new ArgumentNullException(nameof(transactionId)); } @@ -150,7 +148,8 @@ public async Task GetTransactionAsync( response.EnsureAvailableStatusCode(); - return response.Value; + return response.Value ?? throw new InvalidOperationException( + "Failed to deserialize the transaction."); } /// @@ -174,10 +173,10 @@ public async Task DeleteTransactionAsync( /// public async Task DeleteTransactionAsync( string transactionId, - DeleteTransactionOptions options, + DeleteTransactionOptions? options, CancellationToken cancellationToken = default) { - if (transactionId == null) { + if (transactionId is null) { throw new ArgumentNullException(nameof(transactionId)); } @@ -185,7 +184,7 @@ public async Task DeleteTransactionAsync( throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); } - if (options == null) { + if (options is null) { options = new DeleteTransactionOptions(); } @@ -217,11 +216,11 @@ public async Task AddOrReplaceFileMetaToTransactionAsync( string fileId, CancellationToken cancellationToken = default) { - if (fileMeta == null) { + if (fileMeta is null) { throw new ArgumentNullException("fileMeta"); } - if (transactionId == null) { + if (transactionId is null) { throw new ArgumentNullException(nameof(transactionId)); } @@ -229,7 +228,7 @@ public async Task AddOrReplaceFileMetaToTransactionAsync( throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); } - if (fileId == null) { + if (fileId is null) { throw new ArgumentNullException(nameof(fileId)); } @@ -251,7 +250,7 @@ public async Task AddOrReplaceFileToTransactionAsync( Stream fileStream, string transactionId, string fileId, - FileUploadOptions uploadOptions) + FileUploadOptions? uploadOptions) => await AddOrReplaceFileToTransactionAsync( fileStream, transactionId, @@ -264,14 +263,14 @@ public async Task AddOrReplaceFileToTransactionAsync( Stream fileStream, string transactionId, string fileId, - FileUploadOptions uploadOptions, + FileUploadOptions? uploadOptions, CancellationToken cancellationToken = default) { - if (fileStream == null) { + if (fileStream is null) { throw new ArgumentNullException(nameof(fileStream)); } - if (transactionId == null) { + if (transactionId is null) { throw new ArgumentNullException(nameof(transactionId)); } @@ -279,7 +278,7 @@ public async Task AddOrReplaceFileToTransactionAsync( throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); } - if (fileId == null) { + if (fileId is null) { throw new ArgumentNullException(nameof(fileId)); } @@ -287,7 +286,7 @@ public async Task AddOrReplaceFileToTransactionAsync( throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(fileId)); } - if (uploadOptions == null) { + if (uploadOptions is null) { uploadOptions = new FileUploadOptions(); } @@ -322,7 +321,7 @@ public async Task AddOrReplaceFileToTransactionAsync( string filePath, string transactionId, string fileId, - FileUploadOptions uploadOptions) + FileUploadOptions? uploadOptions) => await AddOrReplaceFileToTransactionAsync( filePath, transactionId, @@ -335,10 +334,10 @@ public async Task AddOrReplaceFileToTransactionAsync( string filePath, string transactionId, string fileId, - FileUploadOptions uploadOptions, + FileUploadOptions? uploadOptions, CancellationToken cancellationToken = default) { - if (filePath == null) { + if (filePath is null) { throw new ArgumentNullException(nameof(filePath)); } @@ -382,7 +381,7 @@ public async Task StartTransactionAsync( string transactionId, CancellationToken cancellationToken = default) { - if (transactionId == null) { + if (transactionId is null) { throw new ArgumentNullException(nameof(transactionId)); } @@ -409,7 +408,7 @@ public async Task GetReceiptAsync( string transactionId, CancellationToken cancellationToken = default) { - if (transactionId == null) { + if (transactionId is null) { throw new ArgumentNullException(nameof(transactionId)); } @@ -438,7 +437,7 @@ public async Task GetDocumentAsync( string fileId, CancellationToken cancellationToken = default) { - if (transactionId == null) { + if (transactionId is null) { throw new ArgumentNullException(nameof(transactionId)); } @@ -446,7 +445,7 @@ public async Task GetDocumentAsync( throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); } - if (fileId == null) { + if (fileId is null) { throw new ArgumentNullException(nameof(fileId)); } @@ -479,4 +478,12 @@ protected virtual void Dispose(bool disposing) client?.Dispose(); } } + + private static string? GetVersion() + => typeof(SignhostApiClient) +#if TYPEINFO + .GetTypeInfo() +#endif + ?.Assembly?.GetCustomAttribute() + ?.Version; } diff --git a/src/SignhostAPIClient/Rest/SignHostApiClientSettings.cs b/src/SignhostAPIClient/Rest/SignHostApiClientSettings.cs index c96340d4..15cd1df7 100644 --- a/src/SignhostAPIClient/Rest/SignHostApiClientSettings.cs +++ b/src/SignhostAPIClient/Rest/SignHostApiClientSettings.cs @@ -18,11 +18,11 @@ public SignhostApiClientSettings(string appkey) APPKey = appkey; } - public string UserToken { get; set; } + public string? UserToken { get; set; } public string APPKey { get; private set; } public string Endpoint { get; set; } = DefaultEndpoint; - public Action AddHeader { get; set; } + public Action? AddHeader { get; set; } } diff --git a/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs b/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs index d4fb3bfa..aae80710 100644 --- a/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs +++ b/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Security.Cryptography; using System.Text; @@ -34,17 +35,18 @@ public SignhostApiReceiver(SignhostApiReceiverSettings receiverSettings) public bool IsPostbackChecksumValid( IDictionary headers, string body, - out Transaction postbackTransaction) + [NotNullWhen(true)] out Transaction? postbackTransaction) { postbackTransaction = null; - string postbackChecksum; - string calculatedChecksum; - PostbackTransaction postback; + var postback = DeserializeToPostbackTransaction(body); + if (postback is null) { + return false; + } - postback = DeserializeToPostbackTransaction(body); - postbackChecksum = GetChecksumFromHeadersOrPostback(headers, postback); + string postbackChecksum = GetChecksumFromHeadersOrPostback(headers, postback); bool parametersAreValid = HasValidChecksumProperties(postbackChecksum, postback); + string calculatedChecksum; if (parametersAreValid) { calculatedChecksum = CalculateChecksumFromPostback(postback); postbackTransaction = postback; @@ -66,7 +68,7 @@ private string CalculateChecksumFromPostback(PostbackTransaction postback) } } - private PostbackTransaction DeserializeToPostbackTransaction(string body) + private PostbackTransaction? DeserializeToPostbackTransaction(string body) { return JsonSerializer.Deserialize(body, SignhostJsonSerializerOptions.Default); } @@ -75,8 +77,10 @@ private string GetChecksumFromHeadersOrPostback( IDictionary headers, PostbackTransaction postback) { - string[] postbackChecksumArray; - if (headers.TryGetValue("Checksum", out postbackChecksumArray)) { + if ( + headers.TryGetValue("Checksum", out var postbackChecksumArray) && + postbackChecksumArray is not null + ) { return postbackChecksumArray.First(); } else { diff --git a/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs b/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs index 32e27d47..8867a492 100644 --- a/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs +++ b/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs @@ -31,6 +31,10 @@ public static StreamContent WithDigest( } SetHashValue(fileStream, options); + if (options.DigestHashValue is null) { + throw new InvalidOperationException( + "Digest hash value is not set after calculating it."); + } string base64Digest = Convert.ToBase64String(options.DigestHashValue); @@ -54,7 +58,7 @@ private static void SetHashValue( Stream fileStream, FileDigestOptions options) { - if (options.DigestHashValue != null) { + if (options.DigestHashValue is not null) { return; } diff --git a/src/SignhostAPIClient/SignhostAPIClient.csproj b/src/SignhostAPIClient/SignhostAPIClient.csproj index 7309d334..677e13d7 100644 --- a/src/SignhostAPIClient/SignhostAPIClient.csproj +++ b/src/SignhostAPIClient/SignhostAPIClient.csproj @@ -5,6 +5,7 @@ ../signhost.ruleset TYPEINFO Signhost.APIClient + enable From 90e47cb39720eaef0926f99752013445c64bdbae Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Thu, 27 Nov 2025 21:16:45 +0100 Subject: [PATCH 11/22] Refactor EnsureAvailableStatusCode to be async --- src/SignhostAPIClient/Rest/ApiResponse.cs | 17 +++++++++++------ src/SignhostAPIClient/Rest/SignHostApiClient.cs | 3 ++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/SignhostAPIClient/Rest/ApiResponse.cs b/src/SignhostAPIClient/Rest/ApiResponse.cs index 27a6234f..ac54a922 100644 --- a/src/SignhostAPIClient/Rest/ApiResponse.cs +++ b/src/SignhostAPIClient/Rest/ApiResponse.cs @@ -1,5 +1,7 @@ using System.Net; using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; namespace Signhost.APIClient.Rest; @@ -17,18 +19,21 @@ public ApiResponse(HttpResponseMessage httpResponse, TValue? value) public HttpStatusCode HttpStatusCode => httpResponse.StatusCode; - public void EnsureAvailableStatusCode() + public async Task EnsureAvailableStatusCodeAsync( + CancellationToken cancellationToken = default) { if (HttpStatusCode == HttpStatusCode.Gone) { throw new ErrorHandling.GoneException( httpResponse.ReasonPhrase ?? "No reason phrase provided", Value) { - // TO-DO: Make async in v5 - ResponseBody = httpResponse.Content.ReadAsStringAsync() - .ConfigureAwait(false) - .GetAwaiter() - .GetResult(), + ResponseBody = await httpResponse.Content +#if NETFRAMEWORK || NETSTANDARD2_0 + .ReadAsStringAsync() +#else + .ReadAsStringAsync(cancellationToken) +#endif + .ConfigureAwait(false), }; } } diff --git a/src/SignhostAPIClient/Rest/SignHostApiClient.cs b/src/SignhostAPIClient/Rest/SignHostApiClient.cs index 09adc32e..d06b7189 100644 --- a/src/SignhostAPIClient/Rest/SignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/SignHostApiClient.cs @@ -146,7 +146,8 @@ public async Task GetTransactionAsync( cancellationToken) .ConfigureAwait(false); - response.EnsureAvailableStatusCode(); + await response.EnsureAvailableStatusCodeAsync(cancellationToken) + .ConfigureAwait(false); return response.Value ?? throw new InvalidOperationException( "Failed to deserialize the transaction."); From 64f768383f4e3ad04a86d24ac5f812e371648edf Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Thu, 27 Nov 2025 21:30:01 +0100 Subject: [PATCH 12/22] Add FileFieldType enum --- .../FileFieldTypeTests.cs | 53 +++++++++++++++++++ .../SignhostApiClientTests.cs | 2 +- .../Rest/DataObjects/FileMetaData/Field.cs | 3 +- .../DataObjects/FileMetaData/FileFieldType.cs | 15 ++++++ 4 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 src/SignhostAPIClient.Tests/FileFieldTypeTests.cs create mode 100644 src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileFieldType.cs diff --git a/src/SignhostAPIClient.Tests/FileFieldTypeTests.cs b/src/SignhostAPIClient.Tests/FileFieldTypeTests.cs new file mode 100644 index 00000000..f3afd473 --- /dev/null +++ b/src/SignhostAPIClient.Tests/FileFieldTypeTests.cs @@ -0,0 +1,53 @@ +using System.Text.Json; +using FluentAssertions; +using Signhost.APIClient.Rest.DataObjects; +using Xunit; + +namespace Signhost.APIClient.Rest.Tests; + +public class FileFieldTypeTests +{ + [Theory] + [InlineData(FileFieldType.Seal, "Seal")] + [InlineData(FileFieldType.Signature, "Signature")] + [InlineData(FileFieldType.Check, "Check")] + [InlineData(FileFieldType.Radio, "Radio")] + [InlineData(FileFieldType.SingleLine, "SingleLine")] + [InlineData(FileFieldType.Number, "Number")] + [InlineData(FileFieldType.Date, "Date")] + public void Given_a_field_with_specific_type_When_serialized_to_json_Then_type_is_string_not_numeric(FileFieldType fieldType, string expectedString) + { + // Arrange + var field = new Field + { + Type = fieldType, + Value = "test", + Location = new Location { PageNumber = 1 } + }; + + // Act + var json = JsonSerializer.Serialize(field); + + // Assert + json.Should().Contain($"\"Type\":\"{expectedString}\""); + json.Should().NotContain($"\"Type\":{(int)fieldType}"); + } + + [Fact] + public void Given_json_with_string_field_type_When_deserialized_Then_field_type_enum_is_correctly_parsed() + { + // Arrange + var json = @"{ + ""Type"": ""Signature"", + ""Value"": ""test"", + ""Location"": { ""PageNumber"": 1 } + }"; + + // Act + var field = JsonSerializer.Deserialize(json); + + // Assert + field.Should().NotBeNull(); + field!.Type.Should().Be(FileFieldType.Signature); + } +} diff --git a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs index 80e0fbb7..972b5e7f 100644 --- a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs +++ b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs @@ -41,7 +41,7 @@ public async Task when_AddOrReplaceFileMetaToTransaction_is_called_then_the_requ var field = new Field { - Type = "Check", + Type = FileFieldType.Check, Value = "I agree", Location = new Location { diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Field.cs b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Field.cs index 62f40b0a..755e5049 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Field.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Field.cs @@ -2,8 +2,7 @@ public class Field { - // TO-DO: Make enum in v5. - public string Type { get; set; } = default!; + public FileFieldType Type { get; set; } // TO-DO: Can be boolean, number, string, should be fixed in v5. public string? Value { get; set; } diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileFieldType.cs b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileFieldType.cs new file mode 100644 index 00000000..c5812cb1 --- /dev/null +++ b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileFieldType.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Signhost.APIClient.Rest.DataObjects; + +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum FileFieldType +{ + Seal, + Signature, + Check, + Radio, + SingleLine, + Number, + Date, +} From 304b0e7f1049df2c082daaeb91b6c26cae23c4a5 Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Thu, 27 Nov 2025 21:30:23 +0100 Subject: [PATCH 13/22] Add converter for Field value --- .../FieldValueTests.cs | 274 ++++++++++++++++++ .../Rest/DataObjects/FileMetaData/Field.cs | 12 +- .../JsonConverters/JsonObjectConverter.cs | 55 ++++ 3 files changed, 338 insertions(+), 3 deletions(-) create mode 100644 src/SignhostAPIClient.Tests/FieldValueTests.cs create mode 100644 src/SignhostAPIClient/Rest/JsonConverters/JsonObjectConverter.cs diff --git a/src/SignhostAPIClient.Tests/FieldValueTests.cs b/src/SignhostAPIClient.Tests/FieldValueTests.cs new file mode 100644 index 00000000..4690a696 --- /dev/null +++ b/src/SignhostAPIClient.Tests/FieldValueTests.cs @@ -0,0 +1,274 @@ +using System.Text.Json; +using FluentAssertions; +using Signhost.APIClient.Rest.DataObjects; +using Xunit; + +namespace Signhost.APIClient.Rest.Tests; + +public class FieldValueTests +{ + [Fact] + public void Given_a_field_with_string_value_When_serialized_Then_value_is_json_string() + { + // Arrange + var field = new Field { + Type = FileFieldType.SingleLine, + Value = "John Smith", + Location = new Location { PageNumber = 1 } + }; + + // Act + var json = JsonSerializer.Serialize(field); + + // Assert + json.Should().Contain("\"Value\":\"John Smith\""); + } + + [Fact] + public void Given_a_field_with_numeric_integer_value_When_serialized_Then_value_is_json_number() + { + // Arrange + var field = new Field { + Type = FileFieldType.Number, + Value = 42, + Location = new Location { PageNumber = 1 } + }; + + // Act + var json = JsonSerializer.Serialize(field); + + // Assert + json.Should().Contain("\"Value\":42"); + json.Should().NotContain("\"Value\":\"42\""); + } + + [Fact] + public void Given_a_field_with_numeric_double_value_When_serialized_Then_value_is_json_number() + { + // Arrange + var field = new Field { + Type = FileFieldType.Number, + Value = 3.14, + Location = new Location { PageNumber = 1 } + }; + + // Act + var json = JsonSerializer.Serialize(field); + + // Assert + json.Should().Contain("\"Value\":3.14"); + json.Should().NotContain("\"Value\":\"3.14\""); + } + + [Fact] + public void Given_a_field_with_boolean_true_value_When_serialized_Then_value_is_json_boolean() + { + // Arrange + var field = new Field { + Type = FileFieldType.Check, + Value = true, + Location = new Location { PageNumber = 1 } + }; + + // Act + var json = JsonSerializer.Serialize(field); + + // Assert + json.Should().Contain("\"Value\":true"); + json.Should().NotContain("\"Value\":\"true\""); + } + + [Fact] + public void Given_a_field_with_boolean_false_value_When_serialized_Then_value_is_json_boolean() + { + // Arrange + var field = new Field { + Type = FileFieldType.Check, + Value = false, + Location = new Location { PageNumber = 1 } + }; + + // Act + var json = JsonSerializer.Serialize(field); + + // Assert + json.Should().Contain("\"Value\":false"); + json.Should().NotContain("\"Value\":\"false\""); + } + + [Fact] + public void Given_a_field_with_null_value_When_serialized_Then_value_is_json_null() + { + // Arrange + var field = new Field { + Type = FileFieldType.Signature, + Value = null, + Location = new Location { PageNumber = 1 } + }; + + // Act + var json = JsonSerializer.Serialize(field); + + // Assert + json.Should().Contain("\"Value\":null"); + } + + [Fact] + public void Given_json_with_string_value_When_deserialized_Then_field_value_is_string() + { + // Arrange + var json = @"{ + ""Type"": ""SingleLine"", + ""Value"": ""Test Name"", + ""Location"": { ""PageNumber"": 1 } + }"; + + // Act + var field = JsonSerializer.Deserialize(json); + + // Assert + field.Should().NotBeNull(); + field!.Value.Should().BeOfType().Which.Should().Be("Test Name"); + } + + [Fact] + public void Given_json_with_number_integer_value_When_deserialized_Then_field_value_is_numeric() + { + // Arrange + var json = @"{ + ""Type"": ""Number"", + ""Value"": 123, + ""Location"": { ""PageNumber"": 1 } + }"; + + // Act + var field = JsonSerializer.Deserialize(json); + + // Assert + field.Should().NotBeNull(); + field!.Value.Should().BeOneOf(123L, 123.0); + } + + [Fact] + public void Given_json_with_number_decimal_value_When_deserialized_Then_field_value_is_double() + { + // Arrange + var json = @"{ + ""Type"": ""Number"", + ""Value"": 45.67, + ""Location"": { ""PageNumber"": 1 } + }"; + + // Act + var field = JsonSerializer.Deserialize(json); + + // Assert + field.Should().NotBeNull(); + field!.Value.Should().BeOfType().Which.Should().Be(45.67); + } + + [Fact] + public void Given_json_with_boolean_true_value_When_deserialized_Then_field_value_is_boolean_true() + { + // Arrange + var json = @"{ + ""Type"": ""Check"", + ""Value"": true, + ""Location"": { ""PageNumber"": 1 } + }"; + + // Act + var field = JsonSerializer.Deserialize(json); + + // Assert + field.Should().NotBeNull(); + field!.Value.Should().BeOfType().Which.Should().BeTrue(); + } + + [Fact] + public void Given_json_with_boolean_false_value_When_deserialized_Then_field_value_is_boolean_false() + { + // Arrange + var json = @"{ + ""Type"": ""Check"", + ""Value"": false, + ""Location"": { ""PageNumber"": 1 } + }"; + + // Act + var field = JsonSerializer.Deserialize(json); + + // Assert + field.Should().NotBeNull(); + field!.Value.Should().BeOfType().Which.Should().BeFalse(); + } + + [Fact] + public void Given_json_with_null_value_When_deserialized_Then_field_value_is_null() + { + // Arrange + var json = @"{ + ""Type"": ""Signature"", + ""Value"": null, + ""Location"": { ""PageNumber"": 1 } + }"; + + // Act + var field = JsonSerializer.Deserialize(json); + + // Assert + field.Should().NotBeNull(); + field!.Value.Should().BeNull(); + } + + [Fact] + public void Given_a_field_with_invalid_object_value_When_serialized_Then_throws_json_exception() + { + // Arrange + var field = new Field { + Type = FileFieldType.SingleLine, + Value = new { Name = "Test" }, + Location = new Location { PageNumber = 1 } + }; + + // Act + var act = () => JsonSerializer.Serialize(field); + + // Assert + act.Should().Throw(); + } + + [Fact] + public void Given_json_with_object_value_When_deserialized_Then_throws_json_exception() + { + // Arrange + var json = @"{ + ""Type"": ""SingleLine"", + ""Value"": { ""nested"": ""object"" }, + ""Location"": { ""PageNumber"": 1 } + }"; + + // Act + var act = () => JsonSerializer.Deserialize(json); + + // Assert + act.Should().Throw(); + } + + [Fact] + public void Given_json_with_array_value_When_deserialized_Then_throws_json_exception() + { + // Arrange + var json = @"{ + ""Type"": ""SingleLine"", + ""Value"": [1, 2, 3], + ""Location"": { ""PageNumber"": 1 } + }"; + + // Act + var act = () => JsonSerializer.Deserialize(json); + + // Assert + act.Should().Throw(); + } +} diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Field.cs b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Field.cs index 755e5049..f8ed5bd1 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Field.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Field.cs @@ -1,11 +1,17 @@ -namespace Signhost.APIClient.Rest.DataObjects; +using System.Text.Json.Serialization; +using Signhost.APIClient.Rest.JsonConverters; + +namespace Signhost.APIClient.Rest.DataObjects; public class Field { public FileFieldType Type { get; set; } - // TO-DO: Can be boolean, number, string, should be fixed in v5. - public string? Value { get; set; } + /// + /// The value content for the field. Can be a string, number, or boolean. + /// + [JsonConverter(typeof(JsonObjectConverter))] + public object? Value { get; set; } public Location Location { get; set; } = default!; } diff --git a/src/SignhostAPIClient/Rest/JsonConverters/JsonObjectConverter.cs b/src/SignhostAPIClient/Rest/JsonConverters/JsonObjectConverter.cs new file mode 100644 index 00000000..ceaa0e41 --- /dev/null +++ b/src/SignhostAPIClient/Rest/JsonConverters/JsonObjectConverter.cs @@ -0,0 +1,55 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Signhost.APIClient.Rest.JsonConverters; + +/// +/// Converts JSON values of specific types (string, number, boolean) to/from a C# object. +/// Allows flexible deserialization of fields that can contain string, numeric, or boolean JSON values. +/// +public class JsonObjectConverter + : JsonConverter +{ + public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return reader.TokenType switch + { + JsonTokenType.String => reader.GetString(), + JsonTokenType.Number => reader.TryGetInt64(out var value) + ? value + : reader.GetDouble(), + JsonTokenType.True or JsonTokenType.False => reader.GetBoolean(), + JsonTokenType.Null => null, + _ => throw new JsonException($"Unexpected token {reader.TokenType} when parsing field value. Only string, number, boolean, or null are allowed."), + }; + } + + public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerOptions options) + { + if (value is null) { + writer.WriteNullValue(); + } + else if (value is string s) { + writer.WriteStringValue(s); + } + else if (value is bool b) { + writer.WriteBooleanValue(b); + } + else if (value is int i) { + writer.WriteNumberValue(i); + } + else if (value is long l) { + writer.WriteNumberValue(l); + } + else if (value is double d) { + writer.WriteNumberValue(d); + } + else if (value is decimal dec) { + writer.WriteNumberValue(dec); + } + else { + throw new JsonException($"Field value must be string, number, or boolean, but got {value.GetType().Name}"); + } + } +} From 7f7745d5ff91cb99379d983bb23e75e0d45b12fa Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Thu, 27 Nov 2025 21:36:37 +0100 Subject: [PATCH 14/22] Remove unused activity types --- .../Rest/DataObjects/ActivityType.cs | 90 ++++--------------- 1 file changed, 17 insertions(+), 73 deletions(-) diff --git a/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs b/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs index 223be522..89e3f7b3 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs @@ -1,135 +1,79 @@ namespace Signhost.APIClient.Rest.DataObjects; /// -/// type. +/// type codes as defined in the Signhost API. /// -// TO-DO: Remove unused activity types in v5. public enum ActivityType { /// - /// The invitation mail was sent. + /// Invitation sent (code 101). /// InvitationSent = 101, /// - /// The invitation mail was received. - /// - InvitationReceived = 102, - - /// - /// The sign url was opened. + /// Sign URL was opened (code 103). /// Opened = 103, /// - /// An invitation reminder mail was sent. + /// Invitation reminder sent (code 104). /// InvitationReminderResent = 104, /// - /// The document was opened. - /// The contains the fileId of the opened - /// document. + /// Document was opened. + /// The property contains the file ID of + /// the opened document (code 105). /// DocumentOpened = 105, /// - /// Consumer Signing identity approved. - /// - IdentityApproved = 110, - - /// - /// Consumer Signing identity failed. - /// - IdentityFailed = 111, - - /// - /// Cancelled. - /// - Cancelled = 201, - - /// - /// The signer rejected the sign request. + /// The signer rejected the sign request (code 202). /// Rejected = 202, /// - /// The signer signed the documents. + /// The signer signed the documents (code 203). /// Signed = 203, /// - /// The signer delegated signing to a different signer. + /// The signer delegated signing to a different signer (code 204). /// SignerDelegated = 204, /// - /// Signed document sent. + /// Signed document sent (code 301). /// SignedDocumentSent = 301, /// - /// Signed document opened. + /// Signed document opened (code 302). /// SignedDocumentOpened = 302, /// - /// Signed document downloaded. + /// Signed document downloaded (code 303). /// SignedDocumentDownloaded = 303, /// - /// Receipt sent. + /// Receipt sent (code 401). /// ReceiptSent = 401, /// - /// Receipt opened. + /// Receipt opened (code 402). /// ReceiptOpened = 402, /// - /// Receipt downloaded. + /// Receipt downloaded (code 403). /// ReceiptDownloaded = 403, /// - /// Finished. - /// - Finished = 500, - - /// - /// Deleted. - /// - Deleted = 600, - - /// - /// Expired. - /// - Expired = 700, - - /// - /// Email bounce - hard. - /// - EmailBounceHard = 901, - - /// - /// Email bounce - soft. - /// - EmailBounceSoft = 902, - - /// - /// Email bounce - blocked. - /// - EmailBounceBlocked = 903, - - /// - /// Email bounce - undetermined. - /// - EmailBounceUndetermined = 904, - - /// - /// Operation failed. + /// Transaction failed due to this entity, e.g. email bounce (code 999). /// Failed = 999, } From 8ad4cc36be750e49731b114195f73f38af79eb3b Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Thu, 27 Nov 2025 21:41:13 +0100 Subject: [PATCH 15/22] Refactor use BadAuthorizationException --- src/SignhostAPIClient.Tests/SignhostApiClientTests.cs | 3 ++- .../Rest/ErrorHandling/BadAuthorizationException.cs | 1 - .../HttpResponseMessageErrorHandlingExtensions.cs | 11 ++++------- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs index 972b5e7f..79bdc511 100644 --- a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs +++ b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs @@ -4,6 +4,7 @@ using System.IO; using Xunit; using Signhost.APIClient.Rest.DataObjects; +using Signhost.APIClient.Rest.ErrorHandling; using FluentAssertions; using System.Collections.Generic; using RichardSzalay.MockHttp; @@ -107,7 +108,7 @@ public async Task when_GetTransaction_is_called_and_the_authorization_is_bad_the var signhostApiClient = new SignhostApiClient(settings, httpClient); Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - await getTransaction.Should().ThrowAsync(); + await getTransaction.Should().ThrowAsync(); } mockHttp.VerifyNoOutstandingExpectation(); diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs index d9d8db32..18260ac2 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs @@ -2,7 +2,6 @@ namespace Signhost.APIClient.Rest.ErrorHandling; -// TO-DO: Use this instead of Unauthorized exception in v5 [Serializable] public class BadAuthorizationException : SignhostRestApiClientException diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs b/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs index d43b951f..491c9d4c 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs @@ -24,7 +24,7 @@ public static class HttpResponseMessageErrorHandlingExtensions /// Returns if the call is succesful. /// List of which should /// not be handled as an error. - /// + /// /// When the api authentication failed. /// /// @@ -71,9 +71,8 @@ public static async Task EnsureSignhostSuccessStatusCodeAsy errorMessage = error?.Message ?? "Unknown Signhost error"; } - // TO-DO: Use switch pattern in v5 - Exception exception = response.StatusCode switch { - HttpStatusCode.Unauthorized => new UnauthorizedAccessException(errorMessage), + SignhostRestApiClientException exception = response.StatusCode switch { + HttpStatusCode.Unauthorized => new BadAuthorizationException(errorMessage), HttpStatusCode.BadRequest => new BadRequestException(errorMessage), HttpStatusCode.NotFound => new NotFoundException(errorMessage), @@ -87,9 +86,7 @@ public static async Task EnsureSignhostSuccessStatusCodeAsy _ => new SignhostRestApiClientException(errorMessage), }; - if (exception is SignhostRestApiClientException signhostException) { - signhostException.ResponseBody = responseBody; - } + exception.ResponseBody = responseBody; throw exception; } From 550c7d000aeb8ff360f557f26ca55cef487c1eab Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Thu, 27 Nov 2025 21:51:02 +0100 Subject: [PATCH 16/22] Refactor test ResponseBody in exceptions --- .../SignhostApiClientTests.cs | 106 ++++++++++-------- 1 file changed, 62 insertions(+), 44 deletions(-) diff --git a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs index 79bdc511..031280e0 100644 --- a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs +++ b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs @@ -95,20 +95,22 @@ public async Task when_a_GetTransaction_is_called_then_we_should_have_called_the public async Task when_GetTransaction_is_called_and_the_authorization_is_bad_then_we_should_get_a_BadAuthorizationException() { var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.Unauthorized, new StringContent(""" + const string expectedResponseBody = """ { "message": "unauthorized" } - """)); + """; + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Respond(HttpStatusCode.Unauthorized, new StringContent(expectedResponseBody)); using (var httpClient = mockHttp.ToHttpClient()) { var signhostApiClient = new SignhostApiClient(settings, httpClient); Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - await getTransaction.Should().ThrowAsync(); + var exception = await getTransaction.Should().ThrowAsync(); + exception.Which.ResponseBody.Should().Be(expectedResponseBody); } mockHttp.VerifyNoOutstandingExpectation(); @@ -118,20 +120,22 @@ public async Task when_GetTransaction_is_called_and_the_authorization_is_bad_the public async Task when_GetTransaction_is_called_and_request_is_bad_then_we_should_get_a_BadRequestException() { var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.BadRequest, new StringContent(""" + const string expectedResponseBody = """ { "message": "Bad Request" } - """)); + """; + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Respond(HttpStatusCode.BadRequest, new StringContent(expectedResponseBody)); using (var httpClient = mockHttp.ToHttpClient()) { var signhostApiClient = new SignhostApiClient(settings, httpClient); Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - await getTransaction.Should().ThrowAsync(); + var exception = await getTransaction.Should().ThrowAsync(); + exception.Which.ResponseBody.Should().Be(expectedResponseBody); } mockHttp.VerifyNoOutstandingExpectation(); @@ -141,20 +145,22 @@ public async Task when_GetTransaction_is_called_and_request_is_bad_then_we_shoul public async Task when_GetTransaction_is_called_and_credits_have_run_out_then_we_should_get_a_OutOfCreditsException() { var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.PaymentRequired, new StringContent(""" + const string expectedResponseBody = """ { "type": "https://api.signhost.com/problem/subscription/out-of-credits" } - """)); + """; + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Respond(HttpStatusCode.PaymentRequired, new StringContent(expectedResponseBody)); using (var httpClient = mockHttp.ToHttpClient()) { var signhostApiClient = new SignhostApiClient(settings, httpClient); Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - await getTransaction.Should().ThrowAsync(); + var exception = await getTransaction.Should().ThrowAsync(); + exception.Which.ResponseBody.Should().Be(expectedResponseBody); } mockHttp.VerifyNoOutstandingExpectation(); @@ -164,13 +170,14 @@ public async Task when_GetTransaction_is_called_and_credits_have_run_out_then_we public async Task when_GetTransaction_is_called_and_not_found_then_we_should_get_a_NotFoundException() { var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.NotFound, new StringContent(""" + const string expectedResponseBody = """ { "message": "Not Found" } - """)); + """; + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Respond(HttpStatusCode.NotFound, new StringContent(expectedResponseBody)); using (var httpClient = mockHttp.ToHttpClient()) { @@ -178,7 +185,9 @@ public async Task when_GetTransaction_is_called_and_not_found_then_we_should_get Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - await getTransaction.Should().ThrowAsync().WithMessage("Not Found"); + var exception = await getTransaction.Should().ThrowAsync(); + exception.Which.Message.Should().Be("Not Found"); + exception.Which.ResponseBody.Should().Be(expectedResponseBody); } mockHttp.VerifyNoOutstandingExpectation(); @@ -188,21 +197,23 @@ public async Task when_GetTransaction_is_called_and_not_found_then_we_should_get public async Task when_GetTransaction_is_called_and_unkownerror_like_418_occures_then_we_should_get_a_SignhostException() { var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond((HttpStatusCode)418, new StringContent(""" + const string expectedResponseBody = """ { "message": "418 I'm a teapot" } - """)); + """; + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Respond((HttpStatusCode)418, new StringContent(expectedResponseBody)); using (var httpClient = mockHttp.ToHttpClient()) { var signhostApiClient = new SignhostApiClient(settings, httpClient); Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - await getTransaction.Should().ThrowAsync() - .WithMessage("*418*"); + var exception = await getTransaction.Should().ThrowAsync(); + exception.Which.Message.Should().ContainAll("418"); + exception.Which.ResponseBody.Should().Be(expectedResponseBody); } mockHttp.VerifyNoOutstandingExpectation(); @@ -212,20 +223,22 @@ await getTransaction.Should().ThrowAsync getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - await getTransaction.Should().ThrowAsync(); + var exception = await getTransaction.Should().ThrowAsync(); + exception.Which.ResponseBody.Should().Be(expectedResponseBody); } mockHttp.VerifyNoOutstandingExpectation(); @@ -243,7 +256,8 @@ public async Task When_GetTransaction_is_called_on_gone_transaction_we_shoud_get var signhostApiClient = new SignhostApiClient(settings, httpClient); Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - await getTransaction.Should().ThrowAsync>(); + var exception = await getTransaction.Should().ThrowAsync>(); + exception.Which.ResponseBody.Should().Be(JsonResources.GetTransaction); } mockHttp.VerifyNoOutstandingExpectation(); @@ -318,14 +332,15 @@ public async Task when_a_CreateTransaction_is_called_we_can_add_custom_http_head public async Task when_CreateTransaction_is_called_with_invalid_email_then_we_should_get_a_BadRequestException() { var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Post, "http://localhost/api/transaction") - .WithHeaders("Content-Type", "application/json") - .Respond(HttpStatusCode.BadRequest, new StringContent(""" + const string expectedResponseBody = """ { "message": "Bad Request" } - """)); + """; + mockHttp + .Expect(HttpMethod.Post, "http://localhost/api/transaction") + .WithHeaders("Content-Type", "application/json") + .Respond(HttpStatusCode.BadRequest, new StringContent(expectedResponseBody)); using (var httpClient = mockHttp.ToHttpClient()) { var signhostApiClient = new SignhostApiClient(settings, httpClient); @@ -337,7 +352,8 @@ public async Task when_CreateTransaction_is_called_with_invalid_email_then_we_sh testTransaction.Signers.Add(testSigner); Func getTransaction = () => signhostApiClient.CreateTransactionAsync(testTransaction); - await getTransaction.Should().ThrowAsync(); + var exception = await getTransaction.Should().ThrowAsync(); + exception.Which.ResponseBody.Should().Be(expectedResponseBody); } mockHttp.VerifyNoOutstandingExpectation(); @@ -347,20 +363,22 @@ public async Task when_CreateTransaction_is_called_with_invalid_email_then_we_sh public async Task when_a_function_is_called_with_a_wrong_endpoint_we_should_get_a_SignhostRestApiClientException() { var mockHttp = new MockHttpMessageHandler(); - mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") - .Respond(HttpStatusCode.BadGateway, new StringContent(""" + const string expectedResponseBody = """ { "message": "Bad Gateway" } - """)); + """; + mockHttp + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Respond(HttpStatusCode.BadGateway, new StringContent(expectedResponseBody)); using (var httpClient = mockHttp.ToHttpClient()) { var signhostApiClient = new SignhostApiClient(settings, httpClient); Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - await getTransaction.Should().ThrowAsync() - .WithMessage("Bad Gateway"); + var exception = await getTransaction.Should().ThrowAsync(); + exception.Which.Message.Should().Be("Bad Gateway"); + exception.Which.ResponseBody.Should().Be(expectedResponseBody); } mockHttp.VerifyNoOutstandingExpectation(); From a0ad9558d9b15072ed8a6ed65cf3df928ecb3ed3 Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Thu, 27 Nov 2025 22:02:23 +0100 Subject: [PATCH 17/22] Remove obsolete methods --- .../SignhostApiClientTests.cs | 12 +- .../Rest/ISignHostApiClient.cs | 114 +--------------- .../Rest/SignHostApiClient.cs | 122 +----------------- 3 files changed, 14 insertions(+), 234 deletions(-) diff --git a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs index 031280e0..fe22dbd2 100644 --- a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs +++ b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs @@ -437,7 +437,10 @@ public async Task when_AddOrReplaceFileToTransaction_is_called_then_we_should_ha // Create a 0 sized file using (Stream file = System.IO.File.Create("unittestdocument.pdf")) { - await signhostApiClient.AddOrReplaceFileToTransaction(file, "transaction Id", "file Id"); + await signhostApiClient.AddOrReplaceFileToTransactionAsync( + file, + "transaction Id", + "file Id"); } } @@ -456,7 +459,10 @@ public async Task when_AddOrReplaceFileToTransaction_is_called_default_digest_is using (var httpClient = mockHttp.ToHttpClient()) { var signhostApiClient = new SignhostApiClient(settings, httpClient); - await signhostApiClient.AddOrReplaceFileToTransaction(new MemoryStream(), "transaction Id", "file Id"); + await signhostApiClient.AddOrReplaceFileToTransactionAsync( + new MemoryStream(), + "transaction Id", + "file Id"); } mockHttp.VerifyNoOutstandingExpectation(); @@ -657,7 +663,7 @@ MockedRequest AddHeaders(MockedRequest request) var result = await signhostApiClient.CreateTransactionAsync(new Transaction()); await signhostApiClient.AddOrReplaceFileMetaToTransactionAsync(new FileMeta(), result.Id, "somefileid"); using (Stream file = System.IO.File.Create("unittestdocument.pdf")) { - await signhostApiClient.AddOrReplaceFileToTransaction(file, result.Id, "somefileid"); + await signhostApiClient.AddOrReplaceFileToTransactionAsync(file, result.Id, "somefileid"); } await signhostApiClient.StartTransactionAsync(result.Id); } diff --git a/src/SignhostAPIClient/Rest/ISignHostApiClient.cs b/src/SignhostAPIClient/Rest/ISignHostApiClient.cs index ee2fa040..fb0949fc 100644 --- a/src/SignhostAPIClient/Rest/ISignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/ISignHostApiClient.cs @@ -10,13 +10,6 @@ namespace Signhost.APIClient.Rest; /// public interface ISignhostApiClient { - /// - /// Creates a new transaction. - /// - /// A transaction model. - /// A transaction object. - Task CreateTransactionAsync(Transaction transaction); - /// /// Creates a new transaction. /// @@ -27,21 +20,6 @@ Task CreateTransactionAsync( Transaction transaction, CancellationToken cancellationToken = default); - /// - /// Adds meta data for a file to an existing transaction by providing a - /// file location and a transaction id. - /// - /// Meta data for the file. - /// A valid transaction Id of an existing transaction. - /// An Id for the file. Should be the same as the fileId in the . - /// A task. - /// Make sure to call this method before - /// . - Task AddOrReplaceFileMetaToTransactionAsync( - FileMeta fileMeta, - string transactionId, - string fileId); - /// /// Adds meta data for a file to an existing transaction by providing a /// file location and a transaction id. @@ -59,22 +37,6 @@ Task AddOrReplaceFileMetaToTransactionAsync( string fileId, CancellationToken cancellationToken = default); - /// - /// Add a file to a existing transaction by providing a file location - /// and a transaction id. - /// - /// A Stream containing the file to upload. - /// A valid transaction Id of an existing transaction. - /// A Id for the file. Using the file name is recommended. - /// If a file with the same fileId allready exists the file wil be replaced. - /// . - /// A Task. - Task AddOrReplaceFileToTransactionAsync( - Stream fileStream, - string transactionId, - string fileId, - FileUploadOptions uploadOptions); - /// /// Add a file to a existing transaction by providing a file location /// and a transaction id. @@ -90,25 +52,9 @@ Task AddOrReplaceFileToTransactionAsync( Stream fileStream, string transactionId, string fileId, - FileUploadOptions uploadOptions, + FileUploadOptions? uploadOptions = default, CancellationToken cancellationToken = default); - /// - /// Add a file to a existing transaction by providing a file location - /// and a transaction id. - /// - /// A string representation of the file path. - /// A valid transaction Id of an existing transaction. - /// A Id for the file. Using the file name is recommended. - /// If a file with the same fileId allready exists the file wil be replaced. - /// Optional . - /// A Task. - Task AddOrReplaceFileToTransactionAsync( - string filePath, - string transactionId, - string fileId, - FileUploadOptions uploadOptions); - /// /// Add a file to a existing transaction by providing a file location /// and a transaction id. @@ -124,16 +70,9 @@ Task AddOrReplaceFileToTransactionAsync( string filePath, string transactionId, string fileId, - FileUploadOptions uploadOptions, + FileUploadOptions? uploadOptions = default, CancellationToken cancellationToken = default); - /// - /// start a existing transaction by providing transaction id. - /// - /// A valid transaction Id of an existing transaction. - /// A Task. - Task StartTransactionAsync(string transactionId); - /// /// start a existing transaction by providing transaction id. /// @@ -144,13 +83,6 @@ Task StartTransactionAsync( string transactionId, CancellationToken cancellationToken = default); - /// - /// Gets an exisiting transaction by providing a transaction id. - /// - /// A valid transaction id for an existing transaction. - /// A object. - Task GetTransactionAsync(string transactionId); - /// /// Gets an exisiting transaction by providing a transaction id. /// @@ -161,13 +93,6 @@ Task GetTransactionAsync( string transactionId, CancellationToken cancellationToken = default); - /// - /// Gets a existing transaction by providing a transaction id. - /// - /// A valid transaction Id of an existing transaction. - /// A object. - Task> GetTransactionResponseAsync(string transactionId); - /// /// Gets a existing transaction by providing a transaction id. /// @@ -178,26 +103,6 @@ Task> GetTransactionResponseAsync( string transactionId, CancellationToken cancellationToken = default); - /// - /// Deletes a existing transaction by providing a transaction id. - /// - /// A valid transaction Id of an existing transaction. - /// A cancellation token. - /// A Task. - Task DeleteTransactionAsync( - string transactionId, - CancellationToken cancellationToken = default); - - /// - /// Deletes a existing transaction by providing a transaction id. - /// - /// A valid transaction Id of an existing transaction. - /// Optional . - /// A Task. - Task DeleteTransactionAsync( - string transactionId, - DeleteTransactionOptions options); - /// /// Deletes a existing transaction by providing a transaction id. /// @@ -210,14 +115,6 @@ Task DeleteTransactionAsync( DeleteTransactionOptions? options = default, CancellationToken cancellationToken = default); - /// - /// Gets the signed document of a finished transaction by providing transaction id. - /// - /// A valid transaction Id of an existing transaction. - /// A valid file Id of a signed document. - /// Returns a stream containing the signed document data. - Task GetDocumentAsync(string transactionId, string fileId); - /// /// Gets the signed document of a finished transaction by providing transaction id. /// @@ -230,13 +127,6 @@ Task GetDocumentAsync( string fileId, CancellationToken cancellationToken = default); - /// - /// Gets the receipt of a finished transaction by providing transaction id. - /// - /// A valid transaction Id of an finnished transaction. - /// Returns a stream containing the receipt data. - Task GetReceiptAsync(string transactionId); - /// /// Gets the receipt of a finished transaction by providing transaction id. /// diff --git a/src/SignhostAPIClient/Rest/SignHostApiClient.cs b/src/SignhostAPIClient/Rest/SignHostApiClient.cs index d06b7189..a25fba95 100644 --- a/src/SignhostAPIClient/Rest/SignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/SignHostApiClient.cs @@ -71,12 +71,6 @@ private string ApplicationHeader private string AuthorizationHeader => $"APIKey {settings.UserToken}"; - /// - public async Task CreateTransactionAsync( - Transaction transaction) - => await CreateTransactionAsync(transaction, default) - .ConfigureAwait(false); - /// public async Task CreateTransactionAsync( Transaction transaction, @@ -100,12 +94,6 @@ public async Task CreateTransactionAsync( "Failed to deserialize the transaction."); } - /// - public async Task> GetTransactionResponseAsync( - string transactionId) - => await GetTransactionResponseAsync(transactionId, default) - .ConfigureAwait(false); - /// public async Task> GetTransactionResponseAsync( string transactionId, @@ -131,11 +119,6 @@ public async Task> GetTransactionResponseAsync( return new ApiResponse(result, transaction); } - /// - public async Task GetTransactionAsync(string transactionId) - => await GetTransactionAsync(transactionId, default) - .ConfigureAwait(false); - /// public async Task GetTransactionAsync( string transactionId, @@ -156,25 +139,7 @@ await response.EnsureAvailableStatusCodeAsync(cancellationToken) /// public async Task DeleteTransactionAsync( string transactionId, - CancellationToken cancellationToken = default) - => await DeleteTransactionAsync( - transactionId, - default, - cancellationToken).ConfigureAwait(false); - - /// - public async Task DeleteTransactionAsync( - string transactionId, - DeleteTransactionOptions options) - => await DeleteTransactionAsync( - transactionId, - options, - default).ConfigureAwait(false); - - /// - public async Task DeleteTransactionAsync( - string transactionId, - DeleteTransactionOptions? options, + DeleteTransactionOptions? options = default, CancellationToken cancellationToken = default) { if (transactionId is null) { @@ -199,17 +164,6 @@ await client .ConfigureAwait(false); } - /// - public async Task AddOrReplaceFileMetaToTransactionAsync( - FileMeta fileMeta, - string transactionId, - string fileId) - => await AddOrReplaceFileMetaToTransactionAsync( - fileMeta, - transactionId, - fileId, - default).ConfigureAwait(false); - /// public async Task AddOrReplaceFileMetaToTransactionAsync( FileMeta fileMeta, @@ -246,25 +200,12 @@ await client .ConfigureAwait(false); } - /// - public async Task AddOrReplaceFileToTransactionAsync( - Stream fileStream, - string transactionId, - string fileId, - FileUploadOptions? uploadOptions) - => await AddOrReplaceFileToTransactionAsync( - fileStream, - transactionId, - fileId, - uploadOptions, - default).ConfigureAwait(false); - /// public async Task AddOrReplaceFileToTransactionAsync( Stream fileStream, string transactionId, string fileId, - FileUploadOptions? uploadOptions, + FileUploadOptions? uploadOptions = default, CancellationToken cancellationToken = default) { if (fileStream is null) { @@ -304,38 +245,12 @@ await client .ConfigureAwait(false); } - /// - public Task AddOrReplaceFileToTransaction( - Stream fileStream, - string transactionId, - string fileId) - { - return AddOrReplaceFileToTransactionAsync( - fileStream, - transactionId, - fileId, - null); - } - - /// - public async Task AddOrReplaceFileToTransactionAsync( - string filePath, - string transactionId, - string fileId, - FileUploadOptions? uploadOptions) - => await AddOrReplaceFileToTransactionAsync( - filePath, - transactionId, - fileId, - uploadOptions, - default).ConfigureAwait(false); - /// public async Task AddOrReplaceFileToTransactionAsync( string filePath, string transactionId, string fileId, - FileUploadOptions? uploadOptions, + FileUploadOptions? uploadOptions = default, CancellationToken cancellationToken = default) { if (filePath is null) { @@ -358,25 +273,6 @@ await AddOrReplaceFileToTransactionAsync( } } - /// - public Task AddOrReplaceFileToTransaction( - string filePath, - string transactionId, - string fileId) - { - return AddOrReplaceFileToTransactionAsync( - filePath, - transactionId, - fileId, - null); - } - - /// - public async Task StartTransactionAsync( - string transactionId) - => await StartTransactionAsync(transactionId, default) - .ConfigureAwait(false); - /// public async Task StartTransactionAsync( string transactionId, @@ -399,11 +295,6 @@ await client .ConfigureAwait(false); } - /// - public async Task GetReceiptAsync(string transactionId) - => await GetReceiptAsync(transactionId, default) - .ConfigureAwait(false); - /// public async Task GetReceiptAsync( string transactionId, @@ -425,13 +316,6 @@ public async Task GetReceiptAsync( return result; } - /// - public async Task GetDocumentAsync( - string transactionId, - string fileId) - => await GetDocumentAsync(transactionId, fileId, default) - .ConfigureAwait(false); - /// public async Task GetDocumentAsync( string transactionId, From cb13b3319968fcdb574acc9562a2a95b70b926b5 Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Thu, 27 Nov 2025 22:11:44 +0100 Subject: [PATCH 18/22] Refactor to request objects for transaction creation --- README.md | 6 +- .../TransactionTests.cs | 6 +- .../SignhostApiClientTests.cs | 37 +++-- .../Rest/DataObjects/CreateReceiverRequest.cs | 47 ++++++ .../Rest/DataObjects/CreateSignerRequest.cs | 145 ++++++++++++++++++ .../DataObjects/CreateTransactionRequest.cs | 67 ++++++++ .../Rest/ISignHostApiClient.cs | 4 +- .../Rest/SignHostApiClient.cs | 8 +- 8 files changed, 289 insertions(+), 31 deletions(-) create mode 100644 src/SignhostAPIClient/Rest/DataObjects/CreateReceiverRequest.cs create mode 100644 src/SignhostAPIClient/Rest/DataObjects/CreateSignerRequest.cs create mode 100644 src/SignhostAPIClient/Rest/DataObjects/CreateTransactionRequest.cs diff --git a/README.md b/README.md index 16fa8783..4cfd7176 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,9 @@ var settings = new SignhostApiClientSettings( var client = new SignhostApiClient(settings); -var transaction = await client.CreateTransactionAsync(new Transaction { - Signers = new List { - new Signer { +var transaction = await client.CreateTransactionAsync(new CreateTransactionRequest { + Signers = new List { + new CreateSignerRequest { Email = "john.doe@example.com", SignRequestMessage = "Could you please sign this document?", SendSignRequest = true, diff --git a/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs b/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs index 3bcfb807..bb15303e 100644 --- a/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs +++ b/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs @@ -43,7 +43,7 @@ public async Task Given_complex_transaction_When_created_and_started_Then_all_pr var receiverName = "Jane Receiver"; var receiverReference = "RECEIVER-001"; - var transaction = new Transaction { + var transaction = new CreateTransactionRequest { Seal = false, Reference = testReference, PostbackUrl = testPostbackUrl, @@ -55,7 +55,7 @@ public async Task Given_complex_transaction_When_created_and_started_Then_all_pr TestContext = "integration-test", }, Signers = [ - new Signer { + new CreateSignerRequest { Id = "signer1", Email = signerEmail, Reference = signerReference, @@ -88,7 +88,7 @@ public async Task Given_complex_transaction_When_created_and_started_Then_all_pr } ], Receivers = [ - new Receiver { + new CreateReceiverRequest { Name = receiverName, Email = receiverEmail, Language = "en-US", diff --git a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs index fe22dbd2..93404626 100644 --- a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs +++ b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs @@ -292,10 +292,11 @@ public async Task when_a_CreateTransaction_is_called_then_we_should_have_called_ using (var httpClient = mockHttp.ToHttpClient()) { var signhostApiClient = new SignhostApiClient(settings, httpClient); - Signer testSigner = new Signer(); - testSigner.Email = "firstname.lastname@gmail.com"; + var testSigner = new CreateSignerRequest { + Email = "firstname.lastname@gmail.com" + }; - Transaction testTransaction = new Transaction(); + var testTransaction = new CreateTransactionRequest(); testTransaction.Signers.Add(testSigner); var result = await signhostApiClient.CreateTransactionAsync(testTransaction); @@ -320,7 +321,7 @@ public async Task when_a_CreateTransaction_is_called_we_can_add_custom_http_head var signhostApiClient = new SignhostApiClient(settings, httpClient); - Transaction testTransaction = new Transaction(); + var testTransaction = new CreateTransactionRequest(); var result = await signhostApiClient.CreateTransactionAsync(testTransaction); } @@ -345,10 +346,11 @@ public async Task when_CreateTransaction_is_called_with_invalid_email_then_we_sh using (var httpClient = mockHttp.ToHttpClient()) { var signhostApiClient = new SignhostApiClient(settings, httpClient); - Signer testSigner = new Signer(); - testSigner.Email = "firstname.lastnamegmail.com"; + var testSigner = new CreateSignerRequest { + Email = "firstname.lastnamegmail.com" + }; - Transaction testTransaction = new Transaction(); + var testTransaction = new CreateTransactionRequest(); testTransaction.Signers.Add(testSigner); Func getTransaction = () => signhostApiClient.CreateTransactionAsync(testTransaction); @@ -585,20 +587,17 @@ public async Task When_a_transaction_json_is_returned_it_is_deserialized_correct var signhostApiClient = new SignhostApiClient(settings, httpClient); - var result = await signhostApiClient.CreateTransactionAsync(new Transaction - { - Signers = new List{ - new Signer - { - Verifications = new List - { - new PhoneNumberVerification - { + var result = await signhostApiClient.CreateTransactionAsync(new CreateTransactionRequest { + Signers = [ + new CreateSignerRequest { + Email = "test@example.com", + Verifications = [ + new PhoneNumberVerification { Number = "31615087075" } - } + ] } - } + ] }); result.Id.Should().Be("50262c3f-9744-45bf-a4c6-8a3whatever"); @@ -660,7 +659,7 @@ MockedRequest AddHeaders(MockedRequest request) clientSettings.AddHeader = add => add("X-Custom", "test"); var signhostApiClient = new SignhostApiClient(clientSettings, httpClient); - var result = await signhostApiClient.CreateTransactionAsync(new Transaction()); + var result = await signhostApiClient.CreateTransactionAsync(new CreateTransactionRequest()); await signhostApiClient.AddOrReplaceFileMetaToTransactionAsync(new FileMeta(), result.Id, "somefileid"); using (Stream file = System.IO.File.Create("unittestdocument.pdf")) { await signhostApiClient.AddOrReplaceFileToTransactionAsync(file, result.Id, "somefileid"); diff --git a/src/SignhostAPIClient/Rest/DataObjects/CreateReceiverRequest.cs b/src/SignhostAPIClient/Rest/DataObjects/CreateReceiverRequest.cs new file mode 100644 index 00000000..59a82f0c --- /dev/null +++ b/src/SignhostAPIClient/Rest/DataObjects/CreateReceiverRequest.cs @@ -0,0 +1,47 @@ +namespace Signhost.APIClient.Rest.DataObjects; + +/// +/// Request object for creating a receiver in a transaction. +/// Receiver configuration for getting copies of signed documents. +/// +public class CreateReceiverRequest +{ + /// + /// Gets or sets the receiver's name. + /// + public string? Name { get; set; } + + /// + /// Gets or sets the receiver's email address (required). + /// + public string Email { get; set; } = default!; + + /// + /// Gets or sets the language for receiver communications. + /// Supported values: de-DE, en-US, es-ES, fr-FR, it-IT, pl-PL, nl-NL. + /// Default is nl-NL. + /// + public string? Language { get; set; } + + /// + /// Gets or sets the custom subject for notification email. + /// Maximum of 64 characters allowed. Omitting this parameter will enable the default subject. + /// + public string? Subject { get; set; } + + /// + /// Gets or sets the custom message for notification email (required). + /// Newlines can be created by including a \n in the message. HTML is not allowed. + /// + public string Message { get; set; } = default!; + + /// + /// Gets or sets the custom reference for this receiver. + /// + public string? Reference { get; set; } + + /// + /// Gets or sets the custom receiver data (JSON object only). + /// + public dynamic? Context { get; set; } +} diff --git a/src/SignhostAPIClient/Rest/DataObjects/CreateSignerRequest.cs b/src/SignhostAPIClient/Rest/DataObjects/CreateSignerRequest.cs new file mode 100644 index 00000000..20c2f1a2 --- /dev/null +++ b/src/SignhostAPIClient/Rest/DataObjects/CreateSignerRequest.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; + +namespace Signhost.APIClient.Rest.DataObjects; + +/// +/// Request object for creating a new signer in a transaction. Defines the signer's identity, +/// authentication/verification requirements, notification preferences, and signing behavior. +/// +/// +/// Key Requirements: +/// +/// Email address is mandatory +/// Either Authentications or Verifications must be provided (or both) +/// Signers with AllowDelegation enabled cannot have Authentications +/// SendSignRequest determines if sign request emails are sent automatically +/// +/// +public class CreateSignerRequest +{ + /// + /// Gets or sets the signer identifier (will be generated if not provided). + /// + public string? Id { get; set; } + + /// + /// Gets or sets when the signer's access expires. + /// + public DateTimeOffset? Expires { get; set; } + + /// + /// Gets or sets the signer's email address (required). + /// + public string Email { get; set; } = default!; + + /// + /// Gets or sets the list of authentication methods that the signer has to authenticate with. + /// The order in which the authentications are provided determine in which order the signer will have to perform the specified method. + /// Authentications must be performed before the document(s) can be viewed. + /// + public IList? Authentications { get; set; } + + /// + /// Gets or sets the list of verification methods that the signer has to verify with. + /// The order in which the verifications are provided determine in which order the signer will have to perform the specified method. + /// Verifications must be performed before the document(s) can be signed. + /// + /// + /// Critical Requirement: You must use one of the following verification methods as the last verification in the list: + /// + /// Consent + /// PhoneNumber + /// Scribble + /// CSC Qualified* + /// + /// Important Notes: + /// + /// CSC Qualified must always be the final verification if used + /// The other three methods (Consent, PhoneNumber, Scribble) can be succeeded by other verification methods + /// + /// + public IList? Verifications { get; set; } + + /// + /// Gets or sets a value indicating whether to send sign request to this signer's email address. + /// Default is true. + /// + public bool SendSignRequest { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to send a confirmation email to the signer after signing. + /// Default value is the value of . + /// + public bool? SendSignConfirmation { get; set; } + + /// + /// Gets or sets the subject of the sign request email in plain text. + /// Maximum of 64 characters allowed. If omitted, the default subject will be used. + /// + public string? SignRequestSubject { get; set; } + + /// + /// Gets or sets the message of the sign request email in plain text. HTML is not allowed. + /// Newlines can be created by including a \n. + /// Required if is true. + /// + public string? SignRequestMessage { get; set; } + + /// + /// Gets or sets the number of days between automatic reminder emails sent to this signer. + /// Set to -1 to disable reminders entirely for this signer. + /// Set to 0 to use your organization's default reminder interval. + /// Set to a positive number (e.g., 3, 7) to send reminders every N days. + /// Default is 7. + /// + /// + /// Reminders are only sent if is true and the signer hasn't completed signing yet. + /// + public int? DaysToRemind { get; set; } + + /// + /// Gets or sets the language for signer interface and emails. + /// Supported values: de-DE, en-US, es-ES, fr-FR, it-IT, pl-PL, nl-NL. + /// Default is nl-NL. + /// + public string? Language { get; set; } + + /// + /// Gets or sets the custom reference for this signer. + /// + public string? Reference { get; set; } + + /// + /// Gets or sets the custom introduction text shown to the signer during the signing process. + /// This will be shown on the first screen to the signer and supports limited markdown markup. + /// + /// + /// The following markup is supported: + /// + /// # Headings + /// *Emphasis* / _Emphasis_ + /// **Strong** / __Strong__ + /// 1. Ordered and - Unordered lists + /// + /// + public string? IntroText { get; set; } + + /// + /// Gets or sets the URL to redirect signer after signing. + /// Default is https://signhost.com. + /// + public string? ReturnUrl { get; set; } + + /// + /// Gets or sets a value indicating whether this signer can delegate signing to another person. + /// Cannot be used together with . + /// Default is false. + /// + public bool AllowDelegation { get; set; } + + /// + /// Gets or sets the custom signer data (dynamic JSON object). + /// + public dynamic? Context { get; set; } +} diff --git a/src/SignhostAPIClient/Rest/DataObjects/CreateTransactionRequest.cs b/src/SignhostAPIClient/Rest/DataObjects/CreateTransactionRequest.cs new file mode 100644 index 00000000..7a2e0285 --- /dev/null +++ b/src/SignhostAPIClient/Rest/DataObjects/CreateTransactionRequest.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; + +namespace Signhost.APIClient.Rest.DataObjects; + +/// +/// Request object for creating a new transaction. +/// Transaction creation data including signers, receivers, files, and configuration. +/// +public class CreateTransactionRequest +{ + /// + /// Gets or sets a value indicating whether to seal the transaction (no signers required). + /// When true, the transaction is automatically completed without requiring signatures. + /// + public bool Seal { get; set; } + + /// + /// Gets or sets the custom reference identifier for the transaction. + /// + public string? Reference { get; set; } + + /// + /// Gets or sets the URL to receive status notifications about the transaction. + /// + public string? PostbackUrl { get; set; } + + /// + /// Gets or sets the number of days until the transaction expires. + /// If 0, uses organization default. Maximum value depends on organization settings. + /// + public int DaysToExpire { get; set; } + + /// + /// Gets or sets a value indicating whether to send email notifications to signers and receivers. + /// + public bool SendEmailNotifications { get; set; } + + /// + /// Gets or sets the mode for sign request delivery. + /// 0: No sign requests, 1: Send immediately, 2: Send when ready (default when signers have SendSignRequest enabled). + /// + public int SignRequestMode { get; set; } + + /// + /// Gets or sets the language code for transaction interface and emails. + /// Supported values: de-DE, en-US, es-ES, fr-FR, it-IT, pl-PL, nl-NL. + /// + public string? Language { get; set; } + + /// + /// Gets or sets the custom JSON object for additional transaction data. + /// Only JSON objects are allowed (no arrays or primitives). + /// + public dynamic? Context { get; set; } + + /// + /// Gets or sets the list of signers for the transaction. + /// + public IList Signers { get; set; } = + new List(); + + /// + /// Gets or sets the list of receivers who get copies of completed documents. + /// + public IList Receivers { get; set; } = + new List(); +} diff --git a/src/SignhostAPIClient/Rest/ISignHostApiClient.cs b/src/SignhostAPIClient/Rest/ISignHostApiClient.cs index fb0949fc..ceb27d0d 100644 --- a/src/SignhostAPIClient/Rest/ISignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/ISignHostApiClient.cs @@ -13,11 +13,11 @@ public interface ISignhostApiClient /// /// Creates a new transaction. /// - /// A transaction model. + /// A transaction creation request. /// A cancellation token. /// A transaction object. Task CreateTransactionAsync( - Transaction transaction, + CreateTransactionRequest request, CancellationToken cancellationToken = default); /// diff --git a/src/SignhostAPIClient/Rest/SignHostApiClient.cs b/src/SignhostAPIClient/Rest/SignHostApiClient.cs index a25fba95..e6177097 100644 --- a/src/SignhostAPIClient/Rest/SignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/SignHostApiClient.cs @@ -73,17 +73,17 @@ private string AuthorizationHeader /// public async Task CreateTransactionAsync( - Transaction transaction, + CreateTransactionRequest request, CancellationToken cancellationToken = default) { - if (transaction is null) { - throw new ArgumentNullException(nameof(transaction)); + if (request is null) { + throw new ArgumentNullException(nameof(request)); } var result = await client .PostAsync( "transaction", - JsonContent.From(transaction), + JsonContent.From(request), cancellationToken) .EnsureSignhostSuccessStatusCodeAsync() .ConfigureAwait(false); From bfd5562048509a44633ab0a1fdb11cebabfbca80 Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Thu, 27 Nov 2025 22:12:27 +0100 Subject: [PATCH 19/22] Fix simplify null checks --- .../Rest/SignHostApiClient.cs | 56 +++++-------------- 1 file changed, 14 insertions(+), 42 deletions(-) diff --git a/src/SignhostAPIClient/Rest/SignHostApiClient.cs b/src/SignhostAPIClient/Rest/SignHostApiClient.cs index e6177097..2caa7080 100644 --- a/src/SignhostAPIClient/Rest/SignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/SignHostApiClient.cs @@ -76,9 +76,7 @@ public async Task CreateTransactionAsync( CreateTransactionRequest request, CancellationToken cancellationToken = default) { - if (request is null) { - throw new ArgumentNullException(nameof(request)); - } + ArgumentNullException.ThrowIfNull(request); var result = await client .PostAsync( @@ -99,9 +97,7 @@ public async Task> GetTransactionResponseAsync( string transactionId, CancellationToken cancellationToken = default) { - if (transactionId is null) { - throw new ArgumentNullException(nameof(transactionId)); - } + ArgumentNullException.ThrowIfNull(transactionId); if (string.IsNullOrWhiteSpace(transactionId)) { throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); @@ -142,9 +138,7 @@ public async Task DeleteTransactionAsync( DeleteTransactionOptions? options = default, CancellationToken cancellationToken = default) { - if (transactionId is null) { - throw new ArgumentNullException(nameof(transactionId)); - } + ArgumentNullException.ThrowIfNull(transactionId); if (string.IsNullOrWhiteSpace(transactionId)) { throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); @@ -171,21 +165,15 @@ public async Task AddOrReplaceFileMetaToTransactionAsync( string fileId, CancellationToken cancellationToken = default) { - if (fileMeta is null) { - throw new ArgumentNullException("fileMeta"); - } + ArgumentNullException.ThrowIfNull(fileMeta); - if (transactionId is null) { - throw new ArgumentNullException(nameof(transactionId)); - } + ArgumentNullException.ThrowIfNull(transactionId); if (string.IsNullOrWhiteSpace(transactionId)) { throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); } - if (fileId is null) { - throw new ArgumentNullException(nameof(fileId)); - } + ArgumentNullException.ThrowIfNull(fileId); if (string.IsNullOrWhiteSpace(fileId)) { throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(fileId)); @@ -208,21 +196,15 @@ public async Task AddOrReplaceFileToTransactionAsync( FileUploadOptions? uploadOptions = default, CancellationToken cancellationToken = default) { - if (fileStream is null) { - throw new ArgumentNullException(nameof(fileStream)); - } + ArgumentNullException.ThrowIfNull(fileStream); - if (transactionId is null) { - throw new ArgumentNullException(nameof(transactionId)); - } + ArgumentNullException.ThrowIfNull(transactionId); if (string.IsNullOrWhiteSpace(transactionId)) { throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); } - if (fileId is null) { - throw new ArgumentNullException(nameof(fileId)); - } + ArgumentNullException.ThrowIfNull(fileId); if (string.IsNullOrWhiteSpace(fileId)) { throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(fileId)); @@ -253,9 +235,7 @@ public async Task AddOrReplaceFileToTransactionAsync( FileUploadOptions? uploadOptions = default, CancellationToken cancellationToken = default) { - if (filePath is null) { - throw new ArgumentNullException(nameof(filePath)); - } + ArgumentNullException.ThrowIfNull(filePath); using (Stream fileStream = System.IO.File.Open( filePath, @@ -278,9 +258,7 @@ public async Task StartTransactionAsync( string transactionId, CancellationToken cancellationToken = default) { - if (transactionId is null) { - throw new ArgumentNullException(nameof(transactionId)); - } + ArgumentNullException.ThrowIfNull(transactionId); if (string.IsNullOrWhiteSpace(transactionId)) { throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); @@ -300,9 +278,7 @@ public async Task GetReceiptAsync( string transactionId, CancellationToken cancellationToken = default) { - if (transactionId is null) { - throw new ArgumentNullException(nameof(transactionId)); - } + ArgumentNullException.ThrowIfNull(transactionId); if (string.IsNullOrWhiteSpace(transactionId)) { throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); @@ -322,17 +298,13 @@ public async Task GetDocumentAsync( string fileId, CancellationToken cancellationToken = default) { - if (transactionId is null) { - throw new ArgumentNullException(nameof(transactionId)); - } + ArgumentNullException.ThrowIfNull(transactionId); if (string.IsNullOrWhiteSpace(transactionId)) { throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); } - if (fileId is null) { - throw new ArgumentNullException(nameof(fileId)); - } + ArgumentNullException.ThrowIfNull(fileId); if (string.IsNullOrWhiteSpace(fileId)) { throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(fileId)); From 7ea57a70b0b8a2c9dc3c31cf6825fbde47e13787 Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Thu, 27 Nov 2025 22:28:38 +0100 Subject: [PATCH 20/22] Refactor various style improvements --- .editorconfig | 46 +- README.md | 2 +- .../TransactionTests.cs | 45 +- .../FieldValueTests.cs | 86 +-- .../FileFieldTypeTests.cs | 14 +- .../JSON/JsonResources.cs | 26 +- .../AddOrReplaceFileMetaToTransaction.json | 0 .../JSON/{ => Responses}/AddTransaction.json | 0 .../{ => Responses}/DeleteTransaction.json | 0 .../JSON/{ => Responses}/GetTransaction.json | 0 .../MinimalTransactionResponse.json | 0 .../{ => Responses}/MockPostbackInvalid.json | 0 .../{ => Responses}/MockPostbackValid.json | 0 .../TransactionSingleSignerJson.json | 0 .../LevelEnumConverterTests.cs | 35 +- src/SignhostAPIClient.Tests/PostbackTests.cs | 2 +- .../SignhostAPIClient.Tests.csproj | 2 +- .../SignhostApiClientTests.cs | 617 +++++++++--------- .../SignhostApiReceiverTests.cs | 54 +- src/SignhostAPIClient/Rest/ApiResponse.cs | 7 +- .../Rest/DataObjects/Activity.cs | 2 +- .../Rest/DataObjects/ActivityType.cs | 2 +- .../DataObjects/CreateTransactionRequest.cs | 6 +- .../DataObjects/DeleteTransactionOptions.cs | 2 +- .../Rest/DataObjects/FileEntry.cs | 2 +- .../Rest/DataObjects/FileLink.cs | 2 +- .../Rest/DataObjects/FileMetaData/Field.cs | 2 +- .../Rest/DataObjects/FileMetaData/FileMeta.cs | 2 +- .../FileMetaData/FileSignerMeta.cs | 2 +- .../Rest/DataObjects/FileMetaData/Location.cs | 2 +- .../Rest/DataObjects/Signer.cs | 3 +- .../Rest/DataObjects/Transaction.cs | 9 +- .../Rest/DataObjects/TransactionStatus.cs | 2 +- .../Verifications/ConsentVerification.cs | 2 +- .../Verifications/DigidVerification.cs | 2 +- .../Verifications/IVerification.cs | 2 +- .../Verifications/IdealVerification.cs | 2 +- .../Verifications/IdinVerification.cs | 2 +- .../Verifications/PhoneNumberVerification.cs | 2 +- .../Verifications/ScribbleVerification.cs | 2 +- .../Verifications/SurfnetVerification.cs | 2 +- .../BadAuthorizationException.cs | 2 +- .../Rest/ErrorHandling/NotFoundException.cs | 2 +- .../SignhostRestApiClientException.cs | 2 +- .../Rest/FileDigestOptions.cs | 2 +- .../Rest/FileUploadOptions.cs | 5 +- .../Rest/HttpContentJsonExtensions.cs | 6 +- .../Rest/ISignhostApiClientSettings.cs | 2 +- src/SignhostAPIClient/Rest/JsonContent.cs | 2 +- .../JsonConverters/JsonObjectConverter.cs | 5 +- .../Rest/JsonConverters/LevelEnumConverter.cs | 2 +- .../Rest/SignHostApiClient.cs | 193 +++--- .../Rest/SignHostApiClientSettings.cs | 7 +- .../Rest/SignhostApiReceiver.cs | 43 +- .../Rest/SignhostApiReceiverSettings.cs | 4 +- .../Rest/SignhostJsonSerializerOptions.cs | 14 +- .../StreamContentDigestOptionsExtensions.cs | 13 +- .../Rest/ThrowIfNullExtensions.cs | 26 + .../Rest/UriPathExtensions.cs | 7 - 59 files changed, 674 insertions(+), 653 deletions(-) rename src/SignhostAPIClient.Tests/JSON/{ => Requests}/AddOrReplaceFileMetaToTransaction.json (100%) rename src/SignhostAPIClient.Tests/JSON/{ => Responses}/AddTransaction.json (100%) rename src/SignhostAPIClient.Tests/JSON/{ => Responses}/DeleteTransaction.json (100%) rename src/SignhostAPIClient.Tests/JSON/{ => Responses}/GetTransaction.json (100%) rename src/SignhostAPIClient.Tests/JSON/{ => Responses}/MinimalTransactionResponse.json (100%) rename src/SignhostAPIClient.Tests/JSON/{ => Responses}/MockPostbackInvalid.json (100%) rename src/SignhostAPIClient.Tests/JSON/{ => Responses}/MockPostbackValid.json (100%) rename src/SignhostAPIClient.Tests/JSON/{ => Responses}/TransactionSingleSignerJson.json (100%) create mode 100644 src/SignhostAPIClient/Rest/ThrowIfNullExtensions.cs diff --git a/.editorconfig b/.editorconfig index 122cc175..758aaeaf 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,10 +3,50 @@ root = true [*] charset = utf-8 indent_style = tab +insert_final_newline = true -[*.{cs,js}] +[*.{cs,cshtml,js}] trim_trailing_whitespace = true insert_final_newline = true -[*.csproj] -indent_style = space \ No newline at end of file +dotnet_sort_system_directives_first = true : warning +dotnet_style_predefined_type_for_locals_parameters_members = true : warning +dotnet_style_object_initializer = true : warning +dotnet_style_collection_initializer = true : warning +dotnet_style_explicit_tuple_names = true : error +dotnet_style_coalesce_expression = true : warning +dotnet_style_null_propagation = true : warning + +# Prefer "var" everywhere +csharp_style_var_for_built_in_types = false : suggestion +csharp_style_var_when_type_is_apparent = true : suggestion +csharp_style_var_elsewhere = false : none + +# Prefer method-like constructs to have a block body +csharp_style_expression_bodied_methods = false : none +csharp_style_expression_bodied_constructors = false : none +csharp_style_expression_bodied_operators = false : none + +# Prefer property-like constructs to have an expression-body +csharp_style_expression_bodied_properties = true : none +csharp_style_expression_bodied_indexers = true : none +csharp_style_expression_bodied_accessors = true : none + +# Suggest more modern language features when available +csharp_style_pattern_matching_over_is_with_cast_check = true : suggestion +csharp_style_pattern_matching_over_as_with_null_check = true : suggestion +csharp_style_inlined_variable_declaration = true : suggestion +csharp_style_throw_expression = true : suggestion +csharp_style_conditional_delegate_call = true : suggestion + +# Newline settings +csharp_new_line_before_open_brace = types, methods, properties, indexers, events, event_accessors, anonymous_types, object_collections, array_initializers, local_functions +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true + +[*.{props,targets,config,nuspec,csproj}] +indent_style = tab +indent_size = 2 diff --git a/README.md b/README.md index 4cfd7176..b655563e 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The following code is an example of how to create and start a sign transaction w ```c# var settings = new SignhostApiClientSettings( "AppName appkey", - "apikey or usertoken")); + "apikey or usertoken"); var client = new SignhostApiClient(settings); diff --git a/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs b/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs index bb15303e..99416388 100644 --- a/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs +++ b/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs @@ -18,32 +18,31 @@ public TransactionTests() config = TestConfiguration.Instance; if (!config.IsConfigured) { - throw new InvalidOperationException( - "Integration tests are not configured"); + throw new InvalidOperationException("Integration tests are not configured."); } - var settings = new SignhostApiClientSettings(config.AppKey, config.UserToken) { - Endpoint = config.ApiBaseUrl + SignhostApiClientSettings settings = new(config.AppKey, config.UserToken) { + Endpoint = config.ApiBaseUrl, }; - client = new SignhostApiClient(settings); + client = new(settings); } [Fact] public async Task Given_complex_transaction_When_created_and_started_Then_all_properties_are_correctly_persisted() { // Arrange - var testReference = $"IntegrationTest-{DateTime.UtcNow:yyyyMMddHHmmss}"; - var testPostbackUrl = "https://example.com/postback"; - var signerEmail = "john.doe@example.com"; - var signerReference = "SIGNER-001"; - var signerIntroText = "Please review and sign this document carefully."; + string testReference = $"IntegrationTest-{DateTime.UtcNow:yyyyMMddHHmmss}"; + string testPostbackUrl = "https://example.com/postback"; + string signerEmail = "john.doe@example.com"; + string signerReference = "SIGNER-001"; + string signerIntroText = "Please review and sign this document carefully."; var signerExpires = DateTimeOffset.UtcNow.AddDays(15); - var receiverEmail = "receiver@example.com"; - var receiverName = "Jane Receiver"; - var receiverReference = "RECEIVER-001"; + string receiverEmail = "receiver@example.com"; + string receiverName = "Jane Receiver"; + string receiverReference = "RECEIVER-001"; - var transaction = new CreateTransactionRequest { + CreateTransactionRequest transaction = new() { Seal = false, Reference = testReference, PostbackUrl = testPostbackUrl, @@ -55,7 +54,7 @@ public async Task Given_complex_transaction_When_created_and_started_Then_all_pr TestContext = "integration-test", }, Signers = [ - new CreateSignerRequest { + new() { Id = "signer1", Email = signerEmail, Reference = signerReference, @@ -88,7 +87,7 @@ public async Task Given_complex_transaction_When_created_and_started_Then_all_pr } ], Receivers = [ - new CreateReceiverRequest { + new() { Name = receiverName, Email = receiverEmail, Language = "en-US", @@ -102,7 +101,7 @@ public async Task Given_complex_transaction_When_created_and_started_Then_all_pr ] }; - var pdfPath = Path.Combine("TestFiles", "small-example-pdf-file.pdf"); + string pdfPath = Path.Combine("TestFiles", "small-example-pdf-file.pdf"); if (!File.Exists(pdfPath)) { throw new FileNotFoundException($"Test PDF file not found at: {pdfPath}"); } @@ -121,7 +120,8 @@ public async Task Given_complex_transaction_When_created_and_started_Then_all_pr createdTransaction.SendEmailNotifications.Should().BeFalse(); createdTransaction.SignRequestMode.Should().Be(2); createdTransaction.Language.Should().Be("en-US"); - createdTransaction.CreatedDateTime.Should().BeCloseTo(DateTimeOffset.UtcNow, TimeSpan.FromMinutes(1)); + createdTransaction.CreatedDateTime.Should() + .BeCloseTo(DateTimeOffset.UtcNow, TimeSpan.FromMinutes(1)); createdTransaction.CanceledDateTime.Should().BeNull(); createdTransaction.CancellationReason.Should().BeNull(); @@ -138,7 +138,8 @@ public async Task Given_complex_transaction_When_created_and_started_Then_all_pr createdSigner.Reference.Should().Be(signerReference); createdSigner.IntroText.Should().Be(signerIntroText); createdSigner.Expires.Should().HaveValue(); - createdSigner.Expires.Should().BeCloseTo(signerExpires, TimeSpan.FromMinutes(1)); + createdSigner.Expires.Should() + .BeCloseTo(signerExpires, TimeSpan.FromMinutes(1)); createdSigner.SendSignRequest.Should().BeFalse(); createdSigner.SendSignConfirmation.Should().BeFalse(); createdSigner.DaysToRemind.Should().Be(7); @@ -162,14 +163,16 @@ public async Task Given_complex_transaction_When_created_and_started_Then_all_pr // Assert - Signer Verifications createdSigner.Verifications.Should().HaveCount(1); - var verification = createdSigner.Verifications[0].Should().BeOfType().Subject; + var verification = createdSigner.Verifications[0] + .Should().BeOfType().Subject; verification.ScribbleName.Should().Be("John Doe"); verification.RequireHandsignature.Should().BeTrue(); verification.ScribbleNameFixed.Should().BeTrue(); // Assert - Signer Authentications createdSigner.Authentications.Should().HaveCount(1); - var authentication = createdSigner.Authentications[0].Should().BeOfType().Subject; + var authentication = createdSigner.Authentications[0] + .Should().BeOfType().Subject; authentication.Number.Should().Be("+31612345678"); authentication.SecureDownload.Should().BeTrue(); diff --git a/src/SignhostAPIClient.Tests/FieldValueTests.cs b/src/SignhostAPIClient.Tests/FieldValueTests.cs index 4690a696..f7040381 100644 --- a/src/SignhostAPIClient.Tests/FieldValueTests.cs +++ b/src/SignhostAPIClient.Tests/FieldValueTests.cs @@ -11,14 +11,14 @@ public class FieldValueTests public void Given_a_field_with_string_value_When_serialized_Then_value_is_json_string() { // Arrange - var field = new Field { + Field field = new() { Type = FileFieldType.SingleLine, Value = "John Smith", - Location = new Location { PageNumber = 1 } + Location = new() { PageNumber = 1 }, }; // Act - var json = JsonSerializer.Serialize(field); + string json = JsonSerializer.Serialize(field); // Assert json.Should().Contain("\"Value\":\"John Smith\""); @@ -28,14 +28,14 @@ public void Given_a_field_with_string_value_When_serialized_Then_value_is_json_s public void Given_a_field_with_numeric_integer_value_When_serialized_Then_value_is_json_number() { // Arrange - var field = new Field { + Field field = new() { Type = FileFieldType.Number, Value = 42, - Location = new Location { PageNumber = 1 } + Location = new() { PageNumber = 1 }, }; // Act - var json = JsonSerializer.Serialize(field); + string json = JsonSerializer.Serialize(field); // Assert json.Should().Contain("\"Value\":42"); @@ -46,14 +46,14 @@ public void Given_a_field_with_numeric_integer_value_When_serialized_Then_value_ public void Given_a_field_with_numeric_double_value_When_serialized_Then_value_is_json_number() { // Arrange - var field = new Field { + Field field = new() { Type = FileFieldType.Number, Value = 3.14, - Location = new Location { PageNumber = 1 } + Location = new() { PageNumber = 1 }, }; // Act - var json = JsonSerializer.Serialize(field); + string json = JsonSerializer.Serialize(field); // Assert json.Should().Contain("\"Value\":3.14"); @@ -64,14 +64,14 @@ public void Given_a_field_with_numeric_double_value_When_serialized_Then_value_i public void Given_a_field_with_boolean_true_value_When_serialized_Then_value_is_json_boolean() { // Arrange - var field = new Field { + Field field = new() { Type = FileFieldType.Check, Value = true, - Location = new Location { PageNumber = 1 } + Location = new() { PageNumber = 1 }, }; // Act - var json = JsonSerializer.Serialize(field); + string json = JsonSerializer.Serialize(field); // Assert json.Should().Contain("\"Value\":true"); @@ -82,14 +82,14 @@ public void Given_a_field_with_boolean_true_value_When_serialized_Then_value_is_ public void Given_a_field_with_boolean_false_value_When_serialized_Then_value_is_json_boolean() { // Arrange - var field = new Field { + Field field = new() { Type = FileFieldType.Check, Value = false, - Location = new Location { PageNumber = 1 } + Location = new() { PageNumber = 1 }, }; // Act - var json = JsonSerializer.Serialize(field); + string json = JsonSerializer.Serialize(field); // Assert json.Should().Contain("\"Value\":false"); @@ -100,24 +100,41 @@ public void Given_a_field_with_boolean_false_value_When_serialized_Then_value_is public void Given_a_field_with_null_value_When_serialized_Then_value_is_json_null() { // Arrange - var field = new Field { + Field field = new() { Type = FileFieldType.Signature, Value = null, - Location = new Location { PageNumber = 1 } + Location = new() { PageNumber = 1 }, }; // Act - var json = JsonSerializer.Serialize(field); + string json = JsonSerializer.Serialize(field); // Assert json.Should().Contain("\"Value\":null"); } + [Fact] + public void Given_a_field_with_invalid_object_value_When_serialized_Then_throws_json_exception() + { + // Arrange + Field field = new() { + Type = FileFieldType.SingleLine, + Value = new { Name = "Test" }, + Location = new() { PageNumber = 1 }, + }; + + // Act + var act = () => JsonSerializer.Serialize(field); + + // Assert + act.Should().Throw(); + } + [Fact] public void Given_json_with_string_value_When_deserialized_Then_field_value_is_string() { // Arrange - var json = @"{ + string json = @"{ ""Type"": ""SingleLine"", ""Value"": ""Test Name"", ""Location"": { ""PageNumber"": 1 } @@ -135,7 +152,7 @@ public void Given_json_with_string_value_When_deserialized_Then_field_value_is_s public void Given_json_with_number_integer_value_When_deserialized_Then_field_value_is_numeric() { // Arrange - var json = @"{ + string json = @"{ ""Type"": ""Number"", ""Value"": 123, ""Location"": { ""PageNumber"": 1 } @@ -153,7 +170,7 @@ public void Given_json_with_number_integer_value_When_deserialized_Then_field_va public void Given_json_with_number_decimal_value_When_deserialized_Then_field_value_is_double() { // Arrange - var json = @"{ + string json = @"{ ""Type"": ""Number"", ""Value"": 45.67, ""Location"": { ""PageNumber"": 1 } @@ -171,7 +188,7 @@ public void Given_json_with_number_decimal_value_When_deserialized_Then_field_va public void Given_json_with_boolean_true_value_When_deserialized_Then_field_value_is_boolean_true() { // Arrange - var json = @"{ + string json = @"{ ""Type"": ""Check"", ""Value"": true, ""Location"": { ""PageNumber"": 1 } @@ -189,7 +206,7 @@ public void Given_json_with_boolean_true_value_When_deserialized_Then_field_valu public void Given_json_with_boolean_false_value_When_deserialized_Then_field_value_is_boolean_false() { // Arrange - var json = @"{ + string json = @"{ ""Type"": ""Check"", ""Value"": false, ""Location"": { ""PageNumber"": 1 } @@ -207,7 +224,7 @@ public void Given_json_with_boolean_false_value_When_deserialized_Then_field_val public void Given_json_with_null_value_When_deserialized_Then_field_value_is_null() { // Arrange - var json = @"{ + string json = @"{ ""Type"": ""Signature"", ""Value"": null, ""Location"": { ""PageNumber"": 1 } @@ -221,28 +238,11 @@ public void Given_json_with_null_value_When_deserialized_Then_field_value_is_nul field!.Value.Should().BeNull(); } - [Fact] - public void Given_a_field_with_invalid_object_value_When_serialized_Then_throws_json_exception() - { - // Arrange - var field = new Field { - Type = FileFieldType.SingleLine, - Value = new { Name = "Test" }, - Location = new Location { PageNumber = 1 } - }; - - // Act - var act = () => JsonSerializer.Serialize(field); - - // Assert - act.Should().Throw(); - } - [Fact] public void Given_json_with_object_value_When_deserialized_Then_throws_json_exception() { // Arrange - var json = @"{ + string json = @"{ ""Type"": ""SingleLine"", ""Value"": { ""nested"": ""object"" }, ""Location"": { ""PageNumber"": 1 } @@ -259,7 +259,7 @@ public void Given_json_with_object_value_When_deserialized_Then_throws_json_exce public void Given_json_with_array_value_When_deserialized_Then_throws_json_exception() { // Arrange - var json = @"{ + string json = @"{ ""Type"": ""SingleLine"", ""Value"": [1, 2, 3], ""Location"": { ""PageNumber"": 1 } diff --git a/src/SignhostAPIClient.Tests/FileFieldTypeTests.cs b/src/SignhostAPIClient.Tests/FileFieldTypeTests.cs index f3afd473..76934659 100644 --- a/src/SignhostAPIClient.Tests/FileFieldTypeTests.cs +++ b/src/SignhostAPIClient.Tests/FileFieldTypeTests.cs @@ -15,18 +15,20 @@ public class FileFieldTypeTests [InlineData(FileFieldType.SingleLine, "SingleLine")] [InlineData(FileFieldType.Number, "Number")] [InlineData(FileFieldType.Date, "Date")] - public void Given_a_field_with_specific_type_When_serialized_to_json_Then_type_is_string_not_numeric(FileFieldType fieldType, string expectedString) + public void Given_a_field_with_specific_type_When_serialized_to_json_Then_type_is_string_not_numeric( + FileFieldType fieldType, + string expectedString + ) { // Arrange - var field = new Field - { + Field field = new() { Type = fieldType, Value = "test", - Location = new Location { PageNumber = 1 } + Location = new() { PageNumber = 1 }, }; // Act - var json = JsonSerializer.Serialize(field); + string json = JsonSerializer.Serialize(field); // Assert json.Should().Contain($"\"Type\":\"{expectedString}\""); @@ -37,7 +39,7 @@ public void Given_a_field_with_specific_type_When_serialized_to_json_Then_type_i public void Given_json_with_string_field_type_When_deserialized_Then_field_type_enum_is_correctly_parsed() { // Arrange - var json = @"{ + string json = @"{ ""Type"": ""Signature"", ""Value"": ""test"", ""Location"": { ""PageNumber"": 1 } diff --git a/src/SignhostAPIClient.Tests/JSON/JsonResources.cs b/src/SignhostAPIClient.Tests/JSON/JsonResources.cs index 9bbfbe6e..f0d4f241 100644 --- a/src/SignhostAPIClient.Tests/JSON/JsonResources.cs +++ b/src/SignhostAPIClient.Tests/JSON/JsonResources.cs @@ -5,23 +5,17 @@ namespace SignhostAPIClient.Tests.JSON; public static class JsonResources { - public static string TransactionSingleSignerJson { get; } = - GetJson("TransactionSingleSignerJson"); + // Request JSONs + public static string AddOrReplaceFileMetaToTransaction { get; } = GetJson("Requests.AddOrReplaceFileMetaToTransaction"); - public static string AddOrReplaceFileMetaToTransaction { get; } = - GetJson("AddOrReplaceFileMetaToTransaction"); - public static string AddTransaction { get; } = - GetJson("AddTransaction"); - public static string DeleteTransaction { get; } = - GetJson("DeleteTransaction"); - public static string GetTransaction { get; } = - GetJson("GetTransaction"); - public static string MinimalTransactionResponse { get; } = - GetJson("MinimalTransactionResponse"); - public static string MockPostbackInvalid { get; } = - GetJson("MockPostbackInvalid"); - public static string MockPostbackValid { get; } = - GetJson("MockPostbackValid"); + // Response JSONs + public static string TransactionSingleSignerJson { get; } = GetJson("Responses.TransactionSingleSignerJson"); + public static string AddTransaction { get; } = GetJson("Responses.AddTransaction"); + public static string DeleteTransaction { get; } = GetJson("Responses.DeleteTransaction"); + public static string GetTransaction { get; } = GetJson("Responses.GetTransaction"); + public static string MinimalTransactionResponse { get; } = GetJson("Responses.MinimalTransactionResponse"); + public static string MockPostbackInvalid { get; } = GetJson("Responses.MockPostbackInvalid"); + public static string MockPostbackValid { get; } = GetJson("Responses.MockPostbackValid"); private static string GetJson(string fileName) { diff --git a/src/SignhostAPIClient.Tests/JSON/AddOrReplaceFileMetaToTransaction.json b/src/SignhostAPIClient.Tests/JSON/Requests/AddOrReplaceFileMetaToTransaction.json similarity index 100% rename from src/SignhostAPIClient.Tests/JSON/AddOrReplaceFileMetaToTransaction.json rename to src/SignhostAPIClient.Tests/JSON/Requests/AddOrReplaceFileMetaToTransaction.json diff --git a/src/SignhostAPIClient.Tests/JSON/AddTransaction.json b/src/SignhostAPIClient.Tests/JSON/Responses/AddTransaction.json similarity index 100% rename from src/SignhostAPIClient.Tests/JSON/AddTransaction.json rename to src/SignhostAPIClient.Tests/JSON/Responses/AddTransaction.json diff --git a/src/SignhostAPIClient.Tests/JSON/DeleteTransaction.json b/src/SignhostAPIClient.Tests/JSON/Responses/DeleteTransaction.json similarity index 100% rename from src/SignhostAPIClient.Tests/JSON/DeleteTransaction.json rename to src/SignhostAPIClient.Tests/JSON/Responses/DeleteTransaction.json diff --git a/src/SignhostAPIClient.Tests/JSON/GetTransaction.json b/src/SignhostAPIClient.Tests/JSON/Responses/GetTransaction.json similarity index 100% rename from src/SignhostAPIClient.Tests/JSON/GetTransaction.json rename to src/SignhostAPIClient.Tests/JSON/Responses/GetTransaction.json diff --git a/src/SignhostAPIClient.Tests/JSON/MinimalTransactionResponse.json b/src/SignhostAPIClient.Tests/JSON/Responses/MinimalTransactionResponse.json similarity index 100% rename from src/SignhostAPIClient.Tests/JSON/MinimalTransactionResponse.json rename to src/SignhostAPIClient.Tests/JSON/Responses/MinimalTransactionResponse.json diff --git a/src/SignhostAPIClient.Tests/JSON/MockPostbackInvalid.json b/src/SignhostAPIClient.Tests/JSON/Responses/MockPostbackInvalid.json similarity index 100% rename from src/SignhostAPIClient.Tests/JSON/MockPostbackInvalid.json rename to src/SignhostAPIClient.Tests/JSON/Responses/MockPostbackInvalid.json diff --git a/src/SignhostAPIClient.Tests/JSON/MockPostbackValid.json b/src/SignhostAPIClient.Tests/JSON/Responses/MockPostbackValid.json similarity index 100% rename from src/SignhostAPIClient.Tests/JSON/MockPostbackValid.json rename to src/SignhostAPIClient.Tests/JSON/Responses/MockPostbackValid.json diff --git a/src/SignhostAPIClient.Tests/JSON/TransactionSingleSignerJson.json b/src/SignhostAPIClient.Tests/JSON/Responses/TransactionSingleSignerJson.json similarity index 100% rename from src/SignhostAPIClient.Tests/JSON/TransactionSingleSignerJson.json rename to src/SignhostAPIClient.Tests/JSON/Responses/TransactionSingleSignerJson.json diff --git a/src/SignhostAPIClient.Tests/LevelEnumConverterTests.cs b/src/SignhostAPIClient.Tests/LevelEnumConverterTests.cs index 92ca645a..175ab3f7 100644 --- a/src/SignhostAPIClient.Tests/LevelEnumConverterTests.cs +++ b/src/SignhostAPIClient.Tests/LevelEnumConverterTests.cs @@ -1,9 +1,9 @@ -using FluentAssertions; -using System.Text.Json; -using Signhost.APIClient.Rest.DataObjects; using System; using System.Collections; using System.Collections.Generic; +using System.Text.Json; +using FluentAssertions; +using Signhost.APIClient.Rest.DataObjects; using Xunit; namespace Signhost.APIClient.Rest.Tests; @@ -11,39 +11,48 @@ namespace Signhost.APIClient.Rest.Tests; public class LevelEnumConverterTests { [Fact] - public void when_Level_is_null_should_deserialize_to_null() + public void When_Level_is_null_should_deserialize_to_null() { // Arrange const string json = "{\"Type\":\"eIDAS Login\",\"Level\":null}"; // Act - var eidasLogin = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); + var eidasLogin = JsonSerializer.Deserialize( + json, + SignhostJsonSerializerOptions.Default + ); // Assert eidasLogin.Level.Should().Be(null); } [Fact] - public void when_Level_is_not_supplied_should_deserialize_to_null() + public void When_Level_is_not_supplied_should_deserialize_to_null() { // Arrange const string json = "{\"Type\":\"eIDAS Login\"}"; // Act - var eidasLogin = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); + var eidasLogin = JsonSerializer.Deserialize( + json, + SignhostJsonSerializerOptions.Default + ); // Assert eidasLogin.Level.Should().Be(null); } [Fact] - public void when_Level_is_unknown_should_deserialize_to_Unknown_Level() + public void When_Level_is_unknown_should_deserialize_to_Unknown_Level() { // Arrange const string json = "{\"Type\":\"eIDAS Login\",\"Level\":\"foobar\"}"; // Act - var eidasLogin = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); + var eidasLogin = JsonSerializer.Deserialize( + json, + SignhostJsonSerializerOptions.Default + ); // Assert eidasLogin.Level.Should().Be(Level.Unknown); @@ -51,13 +60,15 @@ public void when_Level_is_unknown_should_deserialize_to_Unknown_Level() [Theory] [ClassData(typeof(LevelTestData))] - public void when_Level_is_valid_should_deserialize_to_correct_value(Level level) + public void When_Level_is_valid_should_deserialize_to_correct_value(Level level) { // Arrange string json = $"{{\"Type\":\"eIDAS Login\",\"Level\":\"{level}\"}}"; // Act - var eidasLogin = JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); + var eidasLogin = JsonSerializer.Deserialize( + json, + SignhostJsonSerializerOptions.Default); // Assert eidasLogin.Level.Should().Be(level); @@ -68,7 +79,7 @@ private class LevelTestData { public IEnumerator GetEnumerator() { - foreach (var value in Enum.GetValues(typeof(Level))) { + foreach (object value in Enum.GetValues()) { yield return new[] { value }; } } diff --git a/src/SignhostAPIClient.Tests/PostbackTests.cs b/src/SignhostAPIClient.Tests/PostbackTests.cs index c469638f..1e08e4df 100644 --- a/src/SignhostAPIClient.Tests/PostbackTests.cs +++ b/src/SignhostAPIClient.Tests/PostbackTests.cs @@ -107,7 +107,7 @@ public void PostbackTransaction_should_get_serialized_correctly() links.Should().HaveCount(1); var link = links.Single(); - link.Rel .Should().Be("file"); + link.Rel.Should().Be("file"); link.Type.Should().Be("application/pdf"); link.Link.Should().Be("https://api.signhost.com/api/transaction/b10ae331-af78-4e79-a39e-5b64693b6b68/file/file1"); } diff --git a/src/SignhostAPIClient.Tests/SignhostAPIClient.Tests.csproj b/src/SignhostAPIClient.Tests/SignhostAPIClient.Tests.csproj index d7aa909c..d4bf700d 100644 --- a/src/SignhostAPIClient.Tests/SignhostAPIClient.Tests.csproj +++ b/src/SignhostAPIClient.Tests/SignhostAPIClient.Tests.csproj @@ -22,7 +22,7 @@ - + diff --git a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs index 93404626..bd3d1dcd 100644 --- a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs +++ b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs @@ -1,245 +1,230 @@ using System; -using System.Threading.Tasks; -using System.Net.Http; +using System.Collections.Generic; using System.IO; -using Xunit; -using Signhost.APIClient.Rest.DataObjects; -using Signhost.APIClient.Rest.ErrorHandling; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; using FluentAssertions; -using System.Collections.Generic; using RichardSzalay.MockHttp; -using System.Net; +using Signhost.APIClient.Rest.DataObjects; +using Signhost.APIClient.Rest.ErrorHandling; using SignhostAPIClient.Tests.JSON; +using Xunit; namespace Signhost.APIClient.Rest.Tests; public class SignhostApiClientTests { private readonly SignhostApiClientSettings settings = new("AppKey", "Usertoken") { - Endpoint = "http://localhost/api/" + Endpoint = "http://localhost/api/", }; private readonly SignhostApiClientSettings oauthSettings = new("AppKey") { - Endpoint = "http://localhost/api/" + Endpoint = "http://localhost/api/", }; [Fact] - public async Task when_AddOrReplaceFileMetaToTransaction_is_called_then_the_request_body_should_contain_the_serialized_file_meta() + public async Task When_AddOrReplaceFileMetaToTransaction_is_called_Then_the_request_body_should_contain_the_serialized_file_meta() { - var mockHttp = new MockHttpMessageHandler(); - - mockHttp.Expect(HttpMethod.Put, "http://localhost/api/transaction/transactionId/file/fileId") + MockHttpMessageHandler mockHttp = new(); + mockHttp + .Expect(HttpMethod.Put, "http://localhost/api/transaction/transactionId/file/fileId") .WithContent(JsonResources.AddOrReplaceFileMetaToTransaction) .Respond(HttpStatusCode.OK); - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); - - var fileSignerMeta = new FileSignerMeta - { - FormSets = new string[] { "SampleFormSet" } - }; - - var field = new Field - { - Type = FileFieldType.Check, - Value = "I agree", - Location = new Location - { - Search = "test" - } - }; - - FileMeta fileMeta = new FileMeta - { - Signers = new Dictionary - { - { "someSignerId", fileSignerMeta } + using var httpClient = mockHttp.ToHttpClient(); + SignhostApiClient signhostApiClient = new(settings, httpClient); + + var fileSignerMeta = new FileSignerMeta { + FormSets = ["SampleFormSet"] + }; + + var field = new Field { + Type = FileFieldType.Check, + Value = "I agree", + Location = new Location { Search = "test" }, + }; + + FileMeta fileMeta = new FileMeta { + Signers = new Dictionary { + ["someSignerId"] = fileSignerMeta, + }, + FormSets = new Dictionary> { + ["SampleFormSet"] = new Dictionary { + ["SampleCheck"] = field, }, - FormSets = new Dictionary> - { - { "SampleFormSet", new Dictionary - { - { "SampleCheck", field } - } - } - } - }; + }, + }; - await signhostApiClient.AddOrReplaceFileMetaToTransactionAsync(fileMeta, "transactionId", "fileId"); - } + await signhostApiClient.AddOrReplaceFileMetaToTransactionAsync( + fileMeta, + "transactionId", + "fileId" + ); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_a_GetTransaction_is_called_then_we_should_have_called_the_transaction_get_once() + public async Task When_a_GetTransaction_is_called_Then_we_should_have_called_the_transaction_get_once() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transactionId") .Respond(HttpStatusCode.OK, new StringContent(JsonResources.GetTransaction)); - using (var httpClient = mockHttp.ToHttpClient()) { - - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using var httpClient = mockHttp.ToHttpClient(); + SignhostApiClient signhostApiClient = new(settings, httpClient); - var result = await signhostApiClient.GetTransactionAsync("transaction Id"); - result.Id.Should().Be("c487be92-0255-40c7-bd7d-20805a65e7d9"); - } + var result = await signhostApiClient.GetTransactionAsync("transactionId"); + result.Id.Should().Be("c487be92-0255-40c7-bd7d-20805a65e7d9"); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_GetTransaction_is_called_and_the_authorization_is_bad_then_we_should_get_a_BadAuthorizationException() + public async Task When_GetTransaction_is_called_and_the_authorization_is_bad_Then_we_should_get_a_BadAuthorizationException() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); const string expectedResponseBody = """ { "message": "unauthorized" } """; mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transactionId") .Respond(HttpStatusCode.Unauthorized, new StringContent(expectedResponseBody)); - using (var httpClient = mockHttp.ToHttpClient()) { + using var httpClient = mockHttp.ToHttpClient(); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + SignhostApiClient signhostApiClient = new(settings, httpClient); - Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - var exception = await getTransaction.Should().ThrowAsync(); - exception.Which.ResponseBody.Should().Be(expectedResponseBody); - } + Func getTransaction = () => signhostApiClient.GetTransactionAsync("transactionId"); + var exception = await getTransaction.Should().ThrowAsync(); + exception.Which.ResponseBody.Should().Be(expectedResponseBody); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_GetTransaction_is_called_and_request_is_bad_then_we_should_get_a_BadRequestException() + public async Task When_GetTransaction_is_called_and_request_is_bad_Then_we_should_get_a_BadRequestException() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); const string expectedResponseBody = """ { "message": "Bad Request" } """; mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transactionId") .Respond(HttpStatusCode.BadRequest, new StringContent(expectedResponseBody)); - using (var httpClient = mockHttp.ToHttpClient()) { + using var httpClient = mockHttp.ToHttpClient(); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + SignhostApiClient signhostApiClient = new(settings, httpClient); - Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - var exception = await getTransaction.Should().ThrowAsync(); - exception.Which.ResponseBody.Should().Be(expectedResponseBody); - } + Func getTransaction = () => signhostApiClient.GetTransactionAsync("transactionId"); + var exception = await getTransaction.Should().ThrowAsync(); + exception.Which.ResponseBody.Should().Be(expectedResponseBody); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_GetTransaction_is_called_and_credits_have_run_out_then_we_should_get_a_OutOfCreditsException() + public async Task When_GetTransaction_is_called_and_credits_have_run_out_Then_we_should_get_a_OutOfCreditsException() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); const string expectedResponseBody = """ { "type": "https://api.signhost.com/problem/subscription/out-of-credits" } """; mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transactionId") .Respond(HttpStatusCode.PaymentRequired, new StringContent(expectedResponseBody)); - using (var httpClient = mockHttp.ToHttpClient()) { + using var httpClient = mockHttp.ToHttpClient(); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + SignhostApiClient signhostApiClient = new(settings, httpClient); - Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - var exception = await getTransaction.Should().ThrowAsync(); - exception.Which.ResponseBody.Should().Be(expectedResponseBody); - } + Func getTransaction = () => signhostApiClient.GetTransactionAsync("transactionId"); + var exception = await getTransaction.Should().ThrowAsync(); + exception.Which.ResponseBody.Should().Be(expectedResponseBody); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_GetTransaction_is_called_and_not_found_then_we_should_get_a_NotFoundException() + public async Task When_GetTransaction_is_called_and_not_found_Then_we_should_get_a_NotFoundException() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); const string expectedResponseBody = """ { "message": "Not Found" } """; mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transactionId") .Respond(HttpStatusCode.NotFound, new StringContent(expectedResponseBody)); - using (var httpClient = mockHttp.ToHttpClient()) { + using var httpClient = mockHttp.ToHttpClient(); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + SignhostApiClient signhostApiClient = new(settings, httpClient); - Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); + Func getTransaction = () => signhostApiClient.GetTransactionAsync("transactionId"); - var exception = await getTransaction.Should().ThrowAsync(); - exception.Which.Message.Should().Be("Not Found"); - exception.Which.ResponseBody.Should().Be(expectedResponseBody); - } + var exception = await getTransaction.Should().ThrowAsync(); + exception.Which.Message.Should().Be("Not Found"); + exception.Which.ResponseBody.Should().Be(expectedResponseBody); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_GetTransaction_is_called_and_unkownerror_like_418_occures_then_we_should_get_a_SignhostException() + public async Task When_GetTransaction_is_called_and_unkownerror_like_418_occures_Then_we_should_get_a_SignhostException() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); const string expectedResponseBody = """ { "message": "418 I'm a teapot" } """; mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transactionId") .Respond((HttpStatusCode)418, new StringContent(expectedResponseBody)); - using (var httpClient = mockHttp.ToHttpClient()) { + using var httpClient = mockHttp.ToHttpClient(); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + SignhostApiClient signhostApiClient = new(settings, httpClient); - Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - var exception = await getTransaction.Should().ThrowAsync(); - exception.Which.Message.Should().ContainAll("418"); - exception.Which.ResponseBody.Should().Be(expectedResponseBody); - } + Func getTransaction = () => signhostApiClient.GetTransactionAsync("transactionId"); + var exception = await getTransaction.Should().ThrowAsync(); + exception.Which.Message.Should().ContainAll("418"); + exception.Which.ResponseBody.Should().Be(expectedResponseBody); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_GetTransaction_is_called_and_there_is_an_InternalServerError_then_we_should_get_a_InternalServerErrorException() + public async Task When_GetTransaction_is_called_and_there_is_an_InternalServerError_Then_we_should_get_a_InternalServerErrorException() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); const string expectedResponseBody = """ { "message": "Internal Server Error" } """; mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transactionId") .Respond(HttpStatusCode.InternalServerError, new StringContent(expectedResponseBody)); - using (var httpClient = mockHttp.ToHttpClient()) { + using var httpClient = mockHttp.ToHttpClient(); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + SignhostApiClient signhostApiClient = new(settings, httpClient); - Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - var exception = await getTransaction.Should().ThrowAsync(); - exception.Which.ResponseBody.Should().Be(expectedResponseBody); - } + Func getTransaction = () => signhostApiClient.GetTransactionAsync("transactionId"); + var exception = await getTransaction.Should().ThrowAsync(); + exception.Which.ResponseBody.Should().Be(expectedResponseBody); mockHttp.VerifyNoOutstandingExpectation(); } @@ -247,18 +232,17 @@ public async Task when_GetTransaction_is_called_and_there_is_an_InternalServerEr [Fact] public async Task When_GetTransaction_is_called_on_gone_transaction_we_shoud_get_a_GoneException() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transactionId") .Respond(HttpStatusCode.Gone, new StringContent(JsonResources.GetTransaction)); - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using var httpClient = mockHttp.ToHttpClient(); + SignhostApiClient signhostApiClient = new(settings, httpClient); - Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - var exception = await getTransaction.Should().ThrowAsync>(); - exception.Which.ResponseBody.Should().Be(JsonResources.GetTransaction); - } + Func getTransaction = () => signhostApiClient.GetTransactionAsync("transactionId"); + var exception = await getTransaction.Should().ThrowAsync>(); + exception.Which.ResponseBody.Should().Be(JsonResources.GetTransaction); mockHttp.VerifyNoOutstandingExpectation(); } @@ -266,73 +250,70 @@ public async Task When_GetTransaction_is_called_on_gone_transaction_we_shoud_get [Fact] public async Task When_GetTransaction_is_called_and_gone_is_expected_we_should_get_a_transaction() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transactionId") .Respond(HttpStatusCode.Gone, new StringContent(JsonResources.GetTransaction)); - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using var httpClient = mockHttp.ToHttpClient(); + SignhostApiClient signhostApiClient = new(settings, httpClient); - Func getTransaction = () => signhostApiClient.GetTransactionResponseAsync("transaction Id"); - await getTransaction.Should().NotThrowAsync(); - } + Func getTransaction = () => signhostApiClient.GetTransactionResponseAsync("transactionId"); + await getTransaction.Should().NotThrowAsync(); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_a_CreateTransaction_is_called_then_we_should_have_called_the_transaction_Post_once() + public async Task When_a_CreateTransaction_is_called_Then_we_should_have_called_the_transaction_Post_once() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); mockHttp .Expect(HttpMethod.Post, "http://localhost/api/transaction") .Respond(HttpStatusCode.OK, new StringContent(JsonResources.AddTransaction)); - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using var httpClient = mockHttp.ToHttpClient(); + SignhostApiClient signhostApiClient = new(settings, httpClient); - var testSigner = new CreateSignerRequest { - Email = "firstname.lastname@gmail.com" - }; + CreateSignerRequest testSigner = new() { + Email = "firstname.lastname@gmail.com", + }; - var testTransaction = new CreateTransactionRequest(); - testTransaction.Signers.Add(testSigner); + CreateTransactionRequest testTransaction = new(); + testTransaction.Signers.Add(testSigner); - var result = await signhostApiClient.CreateTransactionAsync(testTransaction); - result.Id.Should().Be("c487be92-0255-40c7-bd7d-20805a65e7d9"); - } + var result = await signhostApiClient.CreateTransactionAsync(testTransaction); + result.Id.Should().Be("c487be92-0255-40c7-bd7d-20805a65e7d9"); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_a_CreateTransaction_is_called_we_can_add_custom_http_headers() + public async Task When_a_CreateTransaction_is_called_we_can_add_custom_http_headers() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); mockHttp .Expect(HttpMethod.Post, "http://localhost/api/transaction") .WithHeaders("X-Forwarded-For", "localhost") .With(matcher => matcher.Headers.UserAgent.ToString().Contains("SignhostClientLibrary")) .Respond(HttpStatusCode.OK, new StringContent(JsonResources.AddTransaction)); - using (var httpClient = mockHttp.ToHttpClient()) { - settings.AddHeader = (AddHeaders a) => a("X-Forwarded-For", "localhost"); + using var httpClient = mockHttp.ToHttpClient(); + settings.AddHeader = ah => ah("X-Forwarded-For", "localhost"); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + SignhostApiClient signhostApiClient = new(settings, httpClient); - var testTransaction = new CreateTransactionRequest(); + CreateTransactionRequest testTransaction = new(); - var result = await signhostApiClient.CreateTransactionAsync(testTransaction); - } + var result = await signhostApiClient.CreateTransactionAsync(testTransaction); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_CreateTransaction_is_called_with_invalid_email_then_we_should_get_a_BadRequestException() + public async Task When_CreateTransaction_is_called_with_invalid_email_Then_we_should_get_a_BadRequestException() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); const string expectedResponseBody = """ { "message": "Bad Request" @@ -343,235 +324,225 @@ public async Task when_CreateTransaction_is_called_with_invalid_email_then_we_sh .WithHeaders("Content-Type", "application/json") .Respond(HttpStatusCode.BadRequest, new StringContent(expectedResponseBody)); - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using var httpClient = mockHttp.ToHttpClient(); + SignhostApiClient signhostApiClient = new(settings, httpClient); - var testSigner = new CreateSignerRequest { - Email = "firstname.lastnamegmail.com" - }; + CreateSignerRequest testSigner = new() { + Email = "firstname.lastnamegmail.com", + }; - var testTransaction = new CreateTransactionRequest(); - testTransaction.Signers.Add(testSigner); + CreateTransactionRequest testTransaction = new(); + testTransaction.Signers.Add(testSigner); - Func getTransaction = () => signhostApiClient.CreateTransactionAsync(testTransaction); - var exception = await getTransaction.Should().ThrowAsync(); - exception.Which.ResponseBody.Should().Be(expectedResponseBody); - } + Func getTransaction = () => signhostApiClient.CreateTransactionAsync(testTransaction); + var exception = await getTransaction.Should().ThrowAsync(); + exception.Which.ResponseBody.Should().Be(expectedResponseBody); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_a_function_is_called_with_a_wrong_endpoint_we_should_get_a_SignhostRestApiClientException() + public async Task When_a_function_is_called_with_a_wrong_endpoint_we_should_get_a_SignhostRestApiClientException() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); const string expectedResponseBody = """ { "message": "Bad Gateway" } """; mockHttp - .Expect(HttpMethod.Get, "http://localhost/api/transaction/transaction Id") + .Expect(HttpMethod.Get, "http://localhost/api/transaction/transactionId") .Respond(HttpStatusCode.BadGateway, new StringContent(expectedResponseBody)); - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using var httpClient = mockHttp.ToHttpClient(); + SignhostApiClient signhostApiClient = new(settings, httpClient); - Func getTransaction = () => signhostApiClient.GetTransactionAsync("transaction Id"); - var exception = await getTransaction.Should().ThrowAsync(); - exception.Which.Message.Should().Be("Bad Gateway"); - exception.Which.ResponseBody.Should().Be(expectedResponseBody); - } + Func getTransaction = () => signhostApiClient.GetTransactionAsync("transactionId"); + var exception = await getTransaction.Should().ThrowAsync(); + exception.Which.Message.Should().Be("Bad Gateway"); + exception.Which.ResponseBody.Should().Be(expectedResponseBody); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_a_DeleteTransaction_is_called_then_we_should_have_called_the_transaction_delete_once() + public async Task When_a_DeleteTransaction_is_called_Then_we_should_have_called_the_transaction_delete_once() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); mockHttp - .Expect(HttpMethod.Delete, "http://localhost/api/transaction/transaction Id") + .Expect(HttpMethod.Delete, "http://localhost/api/transaction/transactionId") .Respond(HttpStatusCode.OK, new StringContent(JsonResources.DeleteTransaction)); - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using var httpClient = mockHttp.ToHttpClient(); + SignhostApiClient signhostApiClient = new(settings, httpClient); - await signhostApiClient.DeleteTransactionAsync("transaction Id"); - } + await signhostApiClient.DeleteTransactionAsync("transactionId"); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_a_DeleteTransaction_with_notification_is_called_then_we_should_have_called_the_transaction_delete_once() + public async Task When_a_DeleteTransaction_with_notification_is_called_Then_we_should_have_called_the_transaction_delete_once() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); mockHttp - .Expect(HttpMethod.Delete, "http://localhost/api/transaction/transaction Id") + .Expect(HttpMethod.Delete, "http://localhost/api/transaction/transactionId") .WithHeaders("Content-Type", "application/json") //.With(matcher => matcher.Content.ToString().Contains("'SendNotifications': true")) .Respond(HttpStatusCode.OK, new StringContent(JsonResources.DeleteTransaction)); - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using var httpClient = mockHttp.ToHttpClient(); + SignhostApiClient signhostApiClient = new(settings, httpClient); - await signhostApiClient.DeleteTransactionAsync( - "transaction Id", - new DeleteTransactionOptions { SendNotifications = true }); - } + await signhostApiClient.DeleteTransactionAsync( + "transactionId", + new DeleteTransactionOptions { SendNotifications = true }); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_AddOrReplaceFileToTransaction_is_called_then_we_should_have_called_the_file_put_once() + public async Task When_AddOrReplaceFileToTransaction_is_called_Then_we_should_have_called_the_file_put_once() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); mockHttp - .Expect(HttpMethod.Put, "http://localhost/api/transaction/transaction Id/file/file Id") + .Expect(HttpMethod.Put, "http://localhost/api/transaction/transactionId/file/fileId") .WithHeaders("Content-Type", "application/pdf") .WithHeaders("Digest", "SHA-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=") .Respond(HttpStatusCode.OK); - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using var httpClient = mockHttp.ToHttpClient(); + SignhostApiClient signhostApiClient = new(settings, httpClient); - // Create a 0 sized file - using (Stream file = System.IO.File.Create("unittestdocument.pdf")) { - await signhostApiClient.AddOrReplaceFileToTransactionAsync( - file, - "transaction Id", - "file Id"); - } - } + // Create a 0 sized file + using Stream file = File.Create("unittestdocument.pdf"); + await signhostApiClient.AddOrReplaceFileToTransactionAsync( + file, + "transactionId", + "fileId"); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_AddOrReplaceFileToTransaction_is_called_default_digest_is_sha256() + public async Task When_AddOrReplaceFileToTransaction_is_called_default_digest_is_sha256() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); mockHttp - .Expect(HttpMethod.Put, "http://localhost/api/transaction/transaction Id/file/file Id") + .Expect(HttpMethod.Put, "http://localhost/api/transaction/transactionId/file/fileId") .WithHeaders("Digest", "SHA-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=") .Respond(HttpStatusCode.OK); - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using var httpClient = mockHttp.ToHttpClient(); + SignhostApiClient signhostApiClient = new(settings, httpClient); - await signhostApiClient.AddOrReplaceFileToTransactionAsync( - new MemoryStream(), - "transaction Id", - "file Id"); - } + await signhostApiClient.AddOrReplaceFileToTransactionAsync( + new MemoryStream(), + "transactionId", + "fileId"); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_AddOrReplaceFileToTransaction_with_sha512_is_called_default_digest_is_sha512() + public async Task When_AddOrReplaceFileToTransaction_with_sha512_is_called_default_digest_is_sha512() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); mockHttp - .Expect(HttpMethod.Put, "http://localhost/api/transaction/transaction Id/file/file Id") - .WithHeaders("Digest", "SHA-512=z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==") + .Expect(HttpMethod.Put, "http://localhost/api/transaction/transactionId/file/fileId") + .WithHeaders( + "Digest", + "SHA-512=z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==" + ) .Respond(HttpStatusCode.OK); - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); - - await signhostApiClient.AddOrReplaceFileToTransactionAsync( - new MemoryStream(), - "transaction Id", - "file Id", - new FileUploadOptions{ - DigestOptions = new FileDigestOptions - { - DigestHashAlgorithm = DigestHashAlgorithm.SHA512 - } - }); - } + using var httpClient = mockHttp.ToHttpClient(); + SignhostApiClient signhostApiClient = new(settings, httpClient); + + await signhostApiClient.AddOrReplaceFileToTransactionAsync( + new MemoryStream(), + "transactionId", + "fileId", + new FileUploadOptions { + DigestOptions = new FileDigestOptions { + DigestHashAlgorithm = DigestHashAlgorithm.SHA512, + }, + } + ); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_AddOrReplaceFileToTransaction_with_digest_value_is_used_as_is() + public async Task When_AddOrReplaceFileToTransaction_with_digest_value_is_used_as_is() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); mockHttp - .Expect(HttpMethod.Put, "http://localhost/api/transaction/transaction Id/file/file Id") + .Expect(HttpMethod.Put, "http://localhost/api/transaction/transactionId/file/fileId") .WithHeaders("Digest", "SHA-256=AAEC") .Respond(HttpStatusCode.OK); - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); - - await signhostApiClient.AddOrReplaceFileToTransactionAsync( - new MemoryStream(), - "transaction Id", - "file Id", - new FileUploadOptions - { - DigestOptions = new FileDigestOptions - { - DigestHashAlgorithm = DigestHashAlgorithm.SHA256, - DigestHashValue = new byte[] { 0x00, 0x01, 0x02 } - } - }); - } + using var httpClient = mockHttp.ToHttpClient(); + SignhostApiClient signhostApiClient = new(settings, httpClient); + + await signhostApiClient.AddOrReplaceFileToTransactionAsync( + new MemoryStream(), + "transactionId", + "fileId", + new FileUploadOptions { + DigestOptions = new FileDigestOptions { + DigestHashAlgorithm = DigestHashAlgorithm.SHA256, + DigestHashValue = [0x00, 0x01, 0x02], + }, + } + ); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_StartTransaction_is_called_then_we_should_have_called_the_transaction_put_once() + public async Task When_StartTransaction_is_called_Then_we_should_have_called_the_transaction_put_once() { - var mockHttp = new MockHttpMessageHandler(); - mockHttp.Expect("http://localhost/api/transaction/transaction Id/start") + MockHttpMessageHandler mockHttp = new(); + mockHttp.Expect("http://localhost/api/transaction/transactionId/start") .Respond(HttpStatusCode.NoContent); - using (var httpClient = mockHttp.ToHttpClient()) { + using var httpClient = mockHttp.ToHttpClient(); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + SignhostApiClient signhostApiClient = new(settings, httpClient); - await signhostApiClient.StartTransactionAsync("transaction Id"); - } + await signhostApiClient.StartTransactionAsync("transactionId"); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_GetReceipt_is_called_then_we_should_have_called_the_filereceipt_get_once() + public async Task When_GetReceipt_is_called_Then_we_should_have_called_the_filereceipt_get_once() { - var mockHttp = new MockHttpMessageHandler(); - mockHttp.Expect("http://localhost/api/file/receipt/transaction ID") + MockHttpMessageHandler mockHttp = new(); + mockHttp.Expect("http://localhost/api/file/receipt/transactionId") .Respond(HttpStatusCode.OK); - using (var httpClient = mockHttp.ToHttpClient()) { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using var httpClient = mockHttp.ToHttpClient(); + SignhostApiClient signhostApiClient = new(settings, httpClient); - var receipt = await signhostApiClient.GetReceiptAsync("transaction ID"); - } + var receipt = await signhostApiClient.GetReceiptAsync("transactionId"); mockHttp.VerifyNoOutstandingExpectation(); } [Fact] - public async Task when_GetDocument_is_called_then_we_should_have_called_the_file_get_once() + public async Task When_GetDocument_is_called_Then_we_should_have_called_the_file_get_once() { - var mockHttp = new MockHttpMessageHandler(); - mockHttp.Expect(HttpMethod.Get, "http://localhost/api/transaction/*/file/file Id") + MockHttpMessageHandler mockHttp = new(); + mockHttp.Expect(HttpMethod.Get, "http://localhost/api/transaction/*/file/fileId") .Respond(HttpStatusCode.OK, new StringContent(string.Empty)); - using (var httpClient = mockHttp.ToHttpClient()) { + using var httpClient = mockHttp.ToHttpClient(); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + SignhostApiClient signhostApiClient = new(settings, httpClient); - var document = await signhostApiClient.GetDocumentAsync("transaction Id", "file Id"); - } + var document = await signhostApiClient.GetDocumentAsync("transactionId", "fileId"); mockHttp.VerifyNoOutstandingExpectation(); } @@ -579,17 +550,20 @@ public async Task when_GetDocument_is_called_then_we_should_have_called_the_file [Fact] public async Task When_a_transaction_json_is_returned_it_is_deserialized_correctly() { - var mockHttp = new MockHttpMessageHandler(); - mockHttp.Expect(HttpMethod.Post, "http://localhost/api/transaction") - .Respond(HttpStatusCode.OK, new StringContent(JsonResources.TransactionSingleSignerJson)); - - using (var httpClient = mockHttp.ToHttpClient()) { + MockHttpMessageHandler mockHttp = new(); + mockHttp + .Expect(HttpMethod.Post, "http://localhost/api/transaction") + .Respond( + HttpStatusCode.OK, + new StringContent(JsonResources.TransactionSingleSignerJson) + ); - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using var httpClient = mockHttp.ToHttpClient(); + SignhostApiClient signhostApiClient = new(settings, httpClient); - var result = await signhostApiClient.CreateTransactionAsync(new CreateTransactionRequest { - Signers = [ - new CreateSignerRequest { + var result = await signhostApiClient.CreateTransactionAsync(new() { + Signers = [ + new() { Email = "test@example.com", Verifications = [ new PhoneNumberVerification { @@ -597,32 +571,30 @@ public async Task When_a_transaction_json_is_returned_it_is_deserialized_correct } ] } - ] - }); - - result.Id.Should().Be("50262c3f-9744-45bf-a4c6-8a3whatever"); - result.CanceledDateTime.Should().HaveYear(2017); - result.Status.Should().Be(TransactionStatus.WaitingForDocument); - result.Signers.Should().HaveCount(1); - result.Receivers.Should().HaveCount(0); - result.Reference.Should().Be("Contract #123"); - result.SignRequestMode.Should().Be(2); - result.DaysToExpire.Should().Be(14); - result.Signers[0].Id.Should().Be("Signer1"); - result.Signers[0].Email.Should().Be("test1@example.com"); - result.Signers[0].Verifications.Should().HaveCount(1); - result.Signers[0].Verifications[0].Should().BeOfType() - .And.Subject.Should().BeEquivalentTo(new PhoneNumberVerification { - Number = "+31615123456" - }); - result.Signers[0].Activities.Should().HaveCount(3); - result.Signers[0].Activities[0].Should().BeEquivalentTo(new Activity - { - Id = "Activity1", - Code = ActivityType.Opened, - CreatedDateTime = DateTimeOffset.Parse("2017-05-31T22:15:17.6409005+02:00") - }); - } + ] + }); + + result.Id.Should().Be("50262c3f-9744-45bf-a4c6-8a3whatever"); + result.CanceledDateTime.Should().HaveYear(2017); + result.Status.Should().Be(TransactionStatus.WaitingForDocument); + result.Signers.Should().HaveCount(1); + result.Receivers.Should().HaveCount(0); + result.Reference.Should().Be("Contract #123"); + result.SignRequestMode.Should().Be(2); + result.DaysToExpire.Should().Be(14); + result.Signers[0].Id.Should().Be("Signer1"); + result.Signers[0].Email.Should().Be("test1@example.com"); + result.Signers[0].Verifications.Should().HaveCount(1); + result.Signers[0].Verifications[0].Should().BeOfType() + .And.Subject.Should().BeEquivalentTo(new PhoneNumberVerification { + Number = "+31615123456" + }); + result.Signers[0].Activities.Should().HaveCount(3); + result.Signers[0].Activities[0].Should().BeEquivalentTo(new Activity { + Id = "Activity1", + Code = ActivityType.Opened, + CreatedDateTime = DateTimeOffset.Parse("2017-05-31T22:15:17.6409005+02:00") + }); mockHttp.VerifyNoOutstandingExpectation(); } @@ -644,7 +616,7 @@ MockedRequest AddHeaders(MockedRequest request) .WithHeaders("X-Custom", "test"); } - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); AddHeaders(mockHttp.Expect(HttpMethod.Post, "http://localhost/api/transaction")) .Respond(new StringContent(JsonResources.TransactionSingleSignerJson)); AddHeaders(mockHttp.Expect(HttpMethod.Put, "http://localhost/api/transaction/*/file/somefileid")) @@ -654,18 +626,19 @@ MockedRequest AddHeaders(MockedRequest request) AddHeaders(mockHttp.Expect(HttpMethod.Put, "http://localhost/api/transaction/*/start")) .Respond(HttpStatusCode.NoContent); - using (var httpClient = mockHttp.ToHttpClient()) { - var clientSettings = isOauth ? oauthSettings : settings; - clientSettings.AddHeader = add => add("X-Custom", "test"); - var signhostApiClient = new SignhostApiClient(clientSettings, httpClient); + using var httpClient = mockHttp.ToHttpClient(); + var clientSettings = isOauth ? oauthSettings : settings; + clientSettings.AddHeader = add => add("X-Custom", "test"); + SignhostApiClient signhostApiClient = new(clientSettings, httpClient); - var result = await signhostApiClient.CreateTransactionAsync(new CreateTransactionRequest()); - await signhostApiClient.AddOrReplaceFileMetaToTransactionAsync(new FileMeta(), result.Id, "somefileid"); - using (Stream file = System.IO.File.Create("unittestdocument.pdf")) { - await signhostApiClient.AddOrReplaceFileToTransactionAsync(file, result.Id, "somefileid"); - } - await signhostApiClient.StartTransactionAsync(result.Id); - } + var result = await signhostApiClient + .CreateTransactionAsync(new CreateTransactionRequest()); + await signhostApiClient + .AddOrReplaceFileMetaToTransactionAsync(new FileMeta(), result.Id, "somefileid"); + + using Stream file = File.Create("unittestdocument.pdf"); + await signhostApiClient.AddOrReplaceFileToTransactionAsync(file, result.Id, "somefileid"); + await signhostApiClient.StartTransactionAsync(result.Id); mockHttp.VerifyNoOutstandingExpectation(); mockHttp.VerifyNoOutstandingRequest(); @@ -674,20 +647,18 @@ MockedRequest AddHeaders(MockedRequest request) [Fact] public async Task When_a_minimal_response_is_retrieved_list_and_dictionaries_are_not_null() { - var mockHttp = new MockHttpMessageHandler(); + MockHttpMessageHandler mockHttp = new(); mockHttp .Expect(HttpMethod.Get, "http://localhost/api/transaction/c487be92-0255-40c7-bd7d-20805a65e7d9") .Respond(new StringContent(JsonResources.MinimalTransactionResponse)); - using (var httpClient = mockHttp.ToHttpClient()) - { - var signhostApiClient = new SignhostApiClient(settings, httpClient); + using var httpClient = mockHttp.ToHttpClient(); + SignhostApiClient signhostApiClient = new(settings, httpClient); - var result = await signhostApiClient.GetTransactionAsync("c487be92-0255-40c7-bd7d-20805a65e7d9"); + var result = await signhostApiClient.GetTransactionAsync("c487be92-0255-40c7-bd7d-20805a65e7d9"); - result.Signers.Should().BeEmpty(); - result.Receivers.Should().BeEmpty(); - result.Files.Should().BeEmpty(); - } + result.Signers.Should().BeEmpty(); + result.Receivers.Should().BeEmpty(); + result.Files.Should().BeEmpty(); } } diff --git a/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs b/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs index 4bab87eb..81b6bcb4 100644 --- a/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs +++ b/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs @@ -8,69 +8,81 @@ namespace Signhost.APIClient.Rest.Tests; public class SignhostApiReceiverTests { - private SignhostApiReceiverSettings receiverSettings = new SignhostApiReceiverSettings("SharedSecret"); + private readonly SignhostApiReceiverSettings receiverSettings = + new("SharedSecret"); [Fact] - public void when_IsPostbackChecksumValid_is_called_with_valid_postback_in_body_then_true_is_returned() + public void When_IsPostbackChecksumValid_is_called_with_valid_postback_in_body_Then_true_is_returned() { // Arrange - IDictionary headers = new Dictionary { { "Content-Type", new[] { "application/json" } } }; + var headers = new Dictionary { + ["Content-Type"] = ["application/json"] + }; + string body = JsonResources.MockPostbackValid; // Act - SignhostApiReceiver signhostApiReceiver = new SignhostApiReceiver(receiverSettings); - bool result = signhostApiReceiver.IsPostbackChecksumValid(headers, body, out Transaction transaction); + SignhostApiReceiver signhostApiReceiver = new(receiverSettings); + bool result = signhostApiReceiver + .IsPostbackChecksumValid(headers, body, out Transaction _); // Assert result.Should().BeTrue(); } [Fact] - public void when_IsPostbackChecksumValid_is_called_with_invalid_postback_in_body_then_false_is_returned() + public void When_IsPostbackChecksumValid_is_called_with_invalid_postback_in_body_Then_false_is_returned() { // Arrange - IDictionary headers = new Dictionary { { "Content-Type", new[] { "application/json" } } }; + var headers = new Dictionary { + ["Content-Type"] = ["application/json"] + }; + string body = JsonResources.MockPostbackInvalid; // Act - SignhostApiReceiver signhostApiReceiver = new SignhostApiReceiver(receiverSettings); - bool result = signhostApiReceiver.IsPostbackChecksumValid(headers, body, out Transaction transaction); + SignhostApiReceiver signhostApiReceiver = new(receiverSettings); + bool result = signhostApiReceiver + .IsPostbackChecksumValid(headers, body, out Transaction _); // Assert result.Should().BeFalse(); } [Fact] - public void when_IsPostbackChecksumValid_is_called_with_valid_postback_in_header_then_true_is_returned() + public void When_IsPostbackChecksumValid_is_called_with_valid_postback_in_header_Then_true_is_returned() { // Arrange - IDictionary headers = new Dictionary { - { "Content-Type", new[] { "application/json" }}, - {"Checksum", new[] {"cdc09eee2ed6df2846dcc193aedfef59f2834f8d"}} + var headers = new Dictionary { + ["Content-Type"] = ["application/json"], + ["Checksum"] = ["cdc09eee2ed6df2846dcc193aedfef59f2834f8d"] }; + string body = JsonResources.MockPostbackValid; // Act - SignhostApiReceiver signhostApiReceiver = new SignhostApiReceiver(receiverSettings); - bool result = signhostApiReceiver.IsPostbackChecksumValid(headers, body, out Transaction transaction); + SignhostApiReceiver signhostApiReceiver = new(receiverSettings); + bool result = signhostApiReceiver + .IsPostbackChecksumValid(headers, body, out Transaction _); // Assert result.Should().BeTrue(); } [Fact] - public void when_IsPostbackChecksumValid_is_called_with_invalid_postback_in_header_then_false_is_returned() + public void When_IsPostbackChecksumValid_is_called_with_invalid_postback_in_header_Then_false_is_returned() { // Arrange - IDictionary headers = new Dictionary { - { "Content-Type", new[] { "application/json" }}, - {"Checksum", new[] {"70dda90616f744797972c0d2f787f86643a60c83"}} + var headers = new Dictionary { + ["Content-Type"] = ["application/json"], + ["Checksum"] = ["70dda90616f744797972c0d2f787f86643a60c83"] }; string body = JsonResources.MockPostbackValid; // Act - SignhostApiReceiver signhostApiReceiver = new SignhostApiReceiver(receiverSettings); - bool result = signhostApiReceiver.IsPostbackChecksumValid(headers, body, out Transaction transaction); + SignhostApiReceiver signhostApiReceiver = new(receiverSettings); + bool result = signhostApiReceiver + .IsPostbackChecksumValid(headers, body, out Transaction _); // Assert result.Should().BeFalse(); diff --git a/src/SignhostAPIClient/Rest/ApiResponse.cs b/src/SignhostAPIClient/Rest/ApiResponse.cs index ac54a922..0752cb3f 100644 --- a/src/SignhostAPIClient/Rest/ApiResponse.cs +++ b/src/SignhostAPIClient/Rest/ApiResponse.cs @@ -11,8 +11,10 @@ public class ApiResponse public ApiResponse(HttpResponseMessage httpResponse, TValue? value) { + httpResponse.ThrowIfNullOrEmpty(nameof(httpResponse)); + this.httpResponse = httpResponse; - this.Value = value; + Value = value; } public TValue? Value { get; private set; } @@ -25,8 +27,7 @@ public async Task EnsureAvailableStatusCodeAsync( if (HttpStatusCode == HttpStatusCode.Gone) { throw new ErrorHandling.GoneException( httpResponse.ReasonPhrase ?? "No reason phrase provided", - Value) - { + Value) { ResponseBody = await httpResponse.Content #if NETFRAMEWORK || NETSTANDARD2_0 .ReadAsStringAsync() diff --git a/src/SignhostAPIClient/Rest/DataObjects/Activity.cs b/src/SignhostAPIClient/Rest/DataObjects/Activity.cs index c550a2b0..86f4453f 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Activity.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Activity.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Text.Json.Serialization; namespace Signhost.APIClient.Rest.DataObjects; diff --git a/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs b/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs index 89e3f7b3..2ef5d2a7 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs @@ -1,4 +1,4 @@ -namespace Signhost.APIClient.Rest.DataObjects; +namespace Signhost.APIClient.Rest.DataObjects; /// /// type codes as defined in the Signhost API. diff --git a/src/SignhostAPIClient/Rest/DataObjects/CreateTransactionRequest.cs b/src/SignhostAPIClient/Rest/DataObjects/CreateTransactionRequest.cs index 7a2e0285..6d0d57ea 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/CreateTransactionRequest.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/CreateTransactionRequest.cs @@ -56,12 +56,10 @@ public class CreateTransactionRequest /// /// Gets or sets the list of signers for the transaction. /// - public IList Signers { get; set; } = - new List(); + public IList Signers { get; set; } = new List(); /// /// Gets or sets the list of receivers who get copies of completed documents. /// - public IList Receivers { get; set; } = - new List(); + public IList Receivers { get; set; } = new List(); } diff --git a/src/SignhostAPIClient/Rest/DataObjects/DeleteTransactionOptions.cs b/src/SignhostAPIClient/Rest/DataObjects/DeleteTransactionOptions.cs index 5defee9e..f2ec106b 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/DeleteTransactionOptions.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/DeleteTransactionOptions.cs @@ -1,4 +1,4 @@ -namespace Signhost.APIClient.Rest.DataObjects; +namespace Signhost.APIClient.Rest.DataObjects; public class DeleteTransactionOptions { diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileEntry.cs b/src/SignhostAPIClient/Rest/DataObjects/FileEntry.cs index 208c0708..333ad673 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileEntry.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileEntry.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Signhost.APIClient.Rest.DataObjects; diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileLink.cs b/src/SignhostAPIClient/Rest/DataObjects/FileLink.cs index 6fe1ad17..8dced5fc 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileLink.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileLink.cs @@ -1,4 +1,4 @@ -namespace Signhost.APIClient.Rest.DataObjects; +namespace Signhost.APIClient.Rest.DataObjects; public class FileLink { diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Field.cs b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Field.cs index f8ed5bd1..62ed0ac4 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Field.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Field.cs @@ -1,4 +1,4 @@ -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; using Signhost.APIClient.Rest.JsonConverters; namespace Signhost.APIClient.Rest.DataObjects; diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileMeta.cs b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileMeta.cs index 93ab26c1..4212292e 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileMeta.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileMeta.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Text.Json.Serialization; namespace Signhost.APIClient.Rest.DataObjects; diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileSignerMeta.cs b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileSignerMeta.cs index 6ef1c7ff..89b8a6c0 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileSignerMeta.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/FileSignerMeta.cs @@ -1,4 +1,4 @@ -namespace Signhost.APIClient.Rest.DataObjects; +namespace Signhost.APIClient.Rest.DataObjects; public class FileSignerMeta { diff --git a/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Location.cs b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Location.cs index 44926362..997bc211 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Location.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/FileMetaData/Location.cs @@ -1,4 +1,4 @@ -namespace Signhost.APIClient.Rest.DataObjects; +namespace Signhost.APIClient.Rest.DataObjects; public class Location { diff --git a/src/SignhostAPIClient/Rest/DataObjects/Signer.cs b/src/SignhostAPIClient/Rest/DataObjects/Signer.cs index 9b321828..f445f730 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Signer.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Signer.cs @@ -61,8 +61,7 @@ public class Signer public string ReceiptUrl { get; set; } = default!; - public IList Activities { get; set; } = - new List(); + public IList Activities { get; set; } = new List(); public dynamic? Context { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs b/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs index e3953701..089958d1 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs @@ -23,18 +23,15 @@ public class Transaction /// public string? CancellationReason { get; set; } - public IDictionary Files { get; set; } = - new Dictionary(); + public IDictionary Files { get; set; } = new Dictionary(); public TransactionStatus Status { get; set; } public bool Seal { get; set; } - public IList Signers { get; set; } - = new List(); + public IList Signers { get; set; } = new List(); - public IList Receivers { get; set; } - = new List(); + public IList Receivers { get; set; } = new List(); public string? Reference { get; set; } diff --git a/src/SignhostAPIClient/Rest/DataObjects/TransactionStatus.cs b/src/SignhostAPIClient/Rest/DataObjects/TransactionStatus.cs index 7485bf10..a4b62d79 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/TransactionStatus.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/TransactionStatus.cs @@ -1,4 +1,4 @@ -namespace Signhost.APIClient.Rest.DataObjects; +namespace Signhost.APIClient.Rest.DataObjects; public enum TransactionStatus { diff --git a/src/SignhostAPIClient/Rest/DataObjects/Verifications/ConsentVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/ConsentVerification.cs index 6a045ce3..b43c7cef 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Verifications/ConsentVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/ConsentVerification.cs @@ -1,4 +1,4 @@ -namespace Signhost.APIClient.Rest.DataObjects; +namespace Signhost.APIClient.Rest.DataObjects; /// /// Adds a consent verification screen diff --git a/src/SignhostAPIClient/Rest/DataObjects/Verifications/DigidVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/DigidVerification.cs index d0bb4053..2fb6dd2c 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Verifications/DigidVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/DigidVerification.cs @@ -1,4 +1,4 @@ -namespace Signhost.APIClient.Rest.DataObjects; +namespace Signhost.APIClient.Rest.DataObjects; public class DigidVerification : IVerification diff --git a/src/SignhostAPIClient/Rest/DataObjects/Verifications/IVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/IVerification.cs index de6a2977..9c586474 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Verifications/IVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/IVerification.cs @@ -1,4 +1,4 @@ -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace Signhost.APIClient.Rest.DataObjects; diff --git a/src/SignhostAPIClient/Rest/DataObjects/Verifications/IdealVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/IdealVerification.cs index 53600a4a..d13a96ce 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Verifications/IdealVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/IdealVerification.cs @@ -1,4 +1,4 @@ -namespace Signhost.APIClient.Rest.DataObjects; +namespace Signhost.APIClient.Rest.DataObjects; public class IdealVerification : IVerification diff --git a/src/SignhostAPIClient/Rest/DataObjects/Verifications/IdinVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/IdinVerification.cs index f753810c..c0f360ba 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Verifications/IdinVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/IdinVerification.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Signhost.APIClient.Rest.DataObjects; diff --git a/src/SignhostAPIClient/Rest/DataObjects/Verifications/PhoneNumberVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/PhoneNumberVerification.cs index b1c6867c..84e15c75 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Verifications/PhoneNumberVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/PhoneNumberVerification.cs @@ -1,4 +1,4 @@ -namespace Signhost.APIClient.Rest.DataObjects; +namespace Signhost.APIClient.Rest.DataObjects; public class PhoneNumberVerification : IVerification diff --git a/src/SignhostAPIClient/Rest/DataObjects/Verifications/ScribbleVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/ScribbleVerification.cs index 4cb1ee25..8ba484d2 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Verifications/ScribbleVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/ScribbleVerification.cs @@ -1,4 +1,4 @@ -namespace Signhost.APIClient.Rest.DataObjects; +namespace Signhost.APIClient.Rest.DataObjects; public class ScribbleVerification : IVerification diff --git a/src/SignhostAPIClient/Rest/DataObjects/Verifications/SurfnetVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/Verifications/SurfnetVerification.cs index a34adc46..45db66a3 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Verifications/SurfnetVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Verifications/SurfnetVerification.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Signhost.APIClient.Rest.DataObjects; diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs index 18260ac2..5beab9e8 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs @@ -7,7 +7,7 @@ public class BadAuthorizationException : SignhostRestApiClientException { public BadAuthorizationException() - : base("API call returned a 401 error code. Please check your request headers.") + : base("API call returned a 401 error code. Please check your request headers.") { HelpLink = "https://api.signhost.com/Help"; } diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/NotFoundException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/NotFoundException.cs index 0b61c9e3..138cc568 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/NotFoundException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/NotFoundException.cs @@ -7,7 +7,7 @@ public class NotFoundException : SignhostRestApiClientException { public NotFoundException() - : base() + : base() { } diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs index d7e12967..3b4ef903 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs @@ -7,7 +7,7 @@ public class SignhostRestApiClientException : Exception { public SignhostRestApiClientException() - : base() + : base() { } diff --git a/src/SignhostAPIClient/Rest/FileDigestOptions.cs b/src/SignhostAPIClient/Rest/FileDigestOptions.cs index bb5d770f..9493d526 100644 --- a/src/SignhostAPIClient/Rest/FileDigestOptions.cs +++ b/src/SignhostAPIClient/Rest/FileDigestOptions.cs @@ -1,4 +1,4 @@ -namespace Signhost.APIClient.Rest; +namespace Signhost.APIClient.Rest; /// /// File digest options for file uploads. diff --git a/src/SignhostAPIClient/Rest/FileUploadOptions.cs b/src/SignhostAPIClient/Rest/FileUploadOptions.cs index 4336b6d2..09ff94c5 100644 --- a/src/SignhostAPIClient/Rest/FileUploadOptions.cs +++ b/src/SignhostAPIClient/Rest/FileUploadOptions.cs @@ -1,4 +1,4 @@ -namespace Signhost.APIClient.Rest; +namespace Signhost.APIClient.Rest; /// /// Options to be used during a file upload @@ -8,6 +8,5 @@ public class FileUploadOptions /// /// Gets or sets the . /// - public FileDigestOptions DigestOptions { get; set; } - = new FileDigestOptions(); + public FileDigestOptions DigestOptions { get; set; } = new FileDigestOptions(); } diff --git a/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs b/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs index f8759986..94911871 100644 --- a/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs +++ b/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs @@ -16,15 +16,13 @@ internal static class HttpContentJsonExtensions /// to read. /// A deserialized value of /// or default(T) if no content is available. - internal static async Task FromJsonAsync( - this HttpContent httpContent) + internal static async Task FromJsonAsync(this HttpContent httpContent) { if (httpContent is null) { return default; } - var json = await httpContent.ReadAsStringAsync() - .ConfigureAwait(false); + string json = await httpContent.ReadAsStringAsync().ConfigureAwait(false); return JsonSerializer.Deserialize(json, SignhostJsonSerializerOptions.Default); } } diff --git a/src/SignhostAPIClient/Rest/ISignhostApiClientSettings.cs b/src/SignhostAPIClient/Rest/ISignhostApiClientSettings.cs index f017de58..5b423968 100644 --- a/src/SignhostAPIClient/Rest/ISignhostApiClientSettings.cs +++ b/src/SignhostAPIClient/Rest/ISignhostApiClientSettings.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Signhost.APIClient.Rest; diff --git a/src/SignhostAPIClient/Rest/JsonContent.cs b/src/SignhostAPIClient/Rest/JsonContent.cs index cb31cea6..bb29567a 100644 --- a/src/SignhostAPIClient/Rest/JsonContent.cs +++ b/src/SignhostAPIClient/Rest/JsonContent.cs @@ -1,4 +1,4 @@ -using System.Net.Http; +using System.Net.Http; using System.Net.Http.Headers; using System.Text.Json; diff --git a/src/SignhostAPIClient/Rest/JsonConverters/JsonObjectConverter.cs b/src/SignhostAPIClient/Rest/JsonConverters/JsonObjectConverter.cs index ceaa0e41..a604e698 100644 --- a/src/SignhostAPIClient/Rest/JsonConverters/JsonObjectConverter.cs +++ b/src/SignhostAPIClient/Rest/JsonConverters/JsonObjectConverter.cs @@ -16,7 +16,7 @@ public class JsonObjectConverter return reader.TokenType switch { JsonTokenType.String => reader.GetString(), - JsonTokenType.Number => reader.TryGetInt64(out var value) + JsonTokenType.Number => reader.TryGetInt64(out long value) ? value : reader.GetDouble(), JsonTokenType.True or JsonTokenType.False => reader.GetBoolean(), @@ -49,7 +49,8 @@ public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerO writer.WriteNumberValue(dec); } else { - throw new JsonException($"Field value must be string, number, or boolean, but got {value.GetType().Name}"); + throw new JsonException( + $"Field value must be string, number, or boolean, but got {value.GetType().Name}"); } } } diff --git a/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs b/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs index 06b9c0b8..043ceb03 100644 --- a/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs +++ b/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs @@ -22,7 +22,7 @@ internal class LevelEnumConverter } if (reader.TokenType == JsonTokenType.String) { - var value = reader.GetString() ?? string.Empty; + string value = reader.GetString() ?? string.Empty; if (Enum.TryParse(value, out var level)) { return level; } diff --git a/src/SignhostAPIClient/Rest/SignHostApiClient.cs b/src/SignhostAPIClient/Rest/SignHostApiClient.cs index 2caa7080..6361ec6f 100644 --- a/src/SignhostAPIClient/Rest/SignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/SignHostApiClient.cs @@ -47,49 +47,44 @@ public SignhostApiClient( ISignhostApiClientSettings settings, HttpClient httpClient) { + settings.ThrowIfNullOrEmpty(nameof(settings)); + httpClient.ThrowIfNullOrEmpty(nameof(httpClient)); + this.settings = settings; - this.client = httpClient; - this.client.BaseAddress = new Uri( + client = httpClient; + client.BaseAddress = new Uri( settings.Endpoint + (settings.Endpoint.EndsWith("/") ? string.Empty : "/")); - this.client.DefaultRequestHeaders.UserAgent.Add( - new System.Net.Http.Headers.ProductInfoHeaderValue( - "SignhostClientLibrary", - Version)); - this.client.DefaultRequestHeaders.Add("Application", ApplicationHeader); + client.DefaultRequestHeaders.UserAgent.Add( + new ProductInfoHeaderValue("SignhostClientLibrary", Version)); + client.DefaultRequestHeaders.Add("Application", ApplicationHeader); if (!string.IsNullOrWhiteSpace(settings.UserToken)) { - this.client.DefaultRequestHeaders.Add("Authorization", AuthorizationHeader); + client.DefaultRequestHeaders.Add("Authorization", AuthorizationHeader); } - this.client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse($"application/vnd.signhost.{ApiVersion}+json")); - settings.AddHeader?.Invoke(this.client.DefaultRequestHeaders.Add); + client.DefaultRequestHeaders.Accept.Add( + MediaTypeWithQualityHeaderValue.Parse($"application/vnd.signhost.{ApiVersion}+json")); + settings.AddHeader?.Invoke(client.DefaultRequestHeaders.Add); } - private string ApplicationHeader - => $"APPKey {settings.APPKey}"; + private string ApplicationHeader => $"APPKey {settings.APPKey}"; - private string AuthorizationHeader - => $"APIKey {settings.UserToken}"; + private string AuthorizationHeader => $"APIKey {settings.UserToken}"; /// public async Task CreateTransactionAsync( CreateTransactionRequest request, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(request); + request.ThrowIfNullOrEmpty(nameof(request)); var result = await client - .PostAsync( - "transaction", - JsonContent.From(request), - cancellationToken) + .PostAsync("transaction", JsonContent.From(request), cancellationToken) .EnsureSignhostSuccessStatusCodeAsync() .ConfigureAwait(false); - return await result.Content.FromJsonAsync() - .ConfigureAwait(false) - ?? throw new InvalidOperationException( - "Failed to deserialize the transaction."); + return await result.Content.FromJsonAsync().ConfigureAwait(false) + ?? throw new InvalidOperationException("Failed to deserialize the transaction."); } /// @@ -97,20 +92,13 @@ public async Task> GetTransactionResponseAsync( string transactionId, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(transactionId); - - if (string.IsNullOrWhiteSpace(transactionId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); - } + transactionId.ThrowIfNullOrEmpty(nameof(transactionId)); var result = await client - .GetAsync( - "transaction".JoinPaths(transactionId), - cancellationToken) + .GetAsync("transaction".JoinPaths(transactionId), cancellationToken) .EnsureSignhostSuccessStatusCodeAsync(HttpStatusCode.Gone) .ConfigureAwait(false); - var transaction = await result.Content.FromJsonAsync() - .ConfigureAwait(false); + var transaction = await result.Content.FromJsonAsync().ConfigureAwait(false); return new ApiResponse(result, transaction); } @@ -120,16 +108,13 @@ public async Task GetTransactionAsync( string transactionId, CancellationToken cancellationToken = default) { - var response = await GetTransactionResponseAsync( - transactionId, - cancellationToken) + var response = await GetTransactionResponseAsync(transactionId, cancellationToken) .ConfigureAwait(false); - await response.EnsureAvailableStatusCodeAsync(cancellationToken) - .ConfigureAwait(false); + await response.EnsureAvailableStatusCodeAsync(cancellationToken).ConfigureAwait(false); - return response.Value ?? throw new InvalidOperationException( - "Failed to deserialize the transaction."); + return response.Value + ?? throw new InvalidOperationException("Failed to deserialize the transaction."); } /// @@ -138,22 +123,17 @@ public async Task DeleteTransactionAsync( DeleteTransactionOptions? options = default, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(transactionId); - - if (string.IsNullOrWhiteSpace(transactionId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); - } + transactionId.ThrowIfNullOrEmpty(nameof(transactionId)); + options ??= new DeleteTransactionOptions(); - if (options is null) { - options = new DeleteTransactionOptions(); - } + var request = new HttpRequestMessage( + HttpMethod.Delete, + "transaction".JoinPaths(transactionId)) { + Content = JsonContent.From(options), + }; - var request = new HttpRequestMessage(HttpMethod.Delete, "transaction".JoinPaths(transactionId)); - request.Content = JsonContent.From(options); await client - .SendAsync( - request, - cancellationToken) + .SendAsync(request, cancellationToken) .EnsureSignhostSuccessStatusCodeAsync() .ConfigureAwait(false); } @@ -165,19 +145,9 @@ public async Task AddOrReplaceFileMetaToTransactionAsync( string fileId, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(fileMeta); - - ArgumentNullException.ThrowIfNull(transactionId); - - if (string.IsNullOrWhiteSpace(transactionId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); - } - - ArgumentNullException.ThrowIfNull(fileId); - - if (string.IsNullOrWhiteSpace(fileId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(fileId)); - } + fileMeta.ThrowIfNullOrEmpty(nameof(fileMeta)); + transactionId.ThrowIfNullOrEmpty(nameof(transactionId)); + fileId.ThrowIfNullOrEmpty(nameof(fileId)); await client .PutAsync( @@ -196,26 +166,15 @@ public async Task AddOrReplaceFileToTransactionAsync( FileUploadOptions? uploadOptions = default, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(fileStream); + fileStream.ThrowIfNullOrEmpty(nameof(fileStream)); + transactionId.ThrowIfNullOrEmpty(nameof(transactionId)); + fileId.ThrowIfNullOrEmpty(nameof(fileId)); - ArgumentNullException.ThrowIfNull(transactionId); + uploadOptions ??= new FileUploadOptions(); - if (string.IsNullOrWhiteSpace(transactionId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); - } - - ArgumentNullException.ThrowIfNull(fileId); - - if (string.IsNullOrWhiteSpace(fileId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(fileId)); - } - - if (uploadOptions is null) { - uploadOptions = new FileUploadOptions(); - } - - var content = new StreamContent(fileStream) - .WithDigest(fileStream, uploadOptions.DigestOptions); + var content = new StreamContent(fileStream).WithDigest( + fileStream, + uploadOptions.DigestOptions); content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf"); await client @@ -235,22 +194,21 @@ public async Task AddOrReplaceFileToTransactionAsync( FileUploadOptions? uploadOptions = default, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(filePath); - - using (Stream fileStream = System.IO.File.Open( - filePath, - FileMode.Open, - FileAccess.Read, - FileShare.Delete | FileShare.Read)) - { - await AddOrReplaceFileToTransactionAsync( - fileStream, - transactionId, - fileId, - uploadOptions, - cancellationToken) - .ConfigureAwait(false); - } + filePath.ThrowIfNullOrEmpty(nameof(filePath)); + + using Stream fileStream = File.Open( + filePath, + FileMode.Open, + FileAccess.Read, + FileShare.Delete | FileShare.Read); + + await AddOrReplaceFileToTransactionAsync( + fileStream, + transactionId, + fileId, + uploadOptions, + cancellationToken) + .ConfigureAwait(false); } /// @@ -258,17 +216,16 @@ public async Task StartTransactionAsync( string transactionId, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(transactionId); + transactionId.ThrowIfNullOrEmpty(nameof(transactionId)); if (string.IsNullOrWhiteSpace(transactionId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); + throw new ArgumentException( + "Cannot be empty or contain only whitespaces.", + nameof(transactionId)); } await client - .PutAsync( - "transaction".JoinPaths(transactionId, "start"), - null, - cancellationToken) + .PutAsync("transaction".JoinPaths(transactionId, "start"), null, cancellationToken) .EnsureSignhostSuccessStatusCodeAsync() .ConfigureAwait(false); } @@ -278,15 +235,16 @@ public async Task GetReceiptAsync( string transactionId, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(transactionId); + transactionId.ThrowIfNullOrEmpty(nameof(transactionId)); if (string.IsNullOrWhiteSpace(transactionId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); + throw new ArgumentException( + "Cannot be empty or contain only whitespaces.", + nameof(transactionId)); } var result = await client - .GetStreamAsync( - "file".JoinPaths("receipt", transactionId)) + .GetStreamAsync("file".JoinPaths("receipt", transactionId)) .ConfigureAwait(false); return result; @@ -298,21 +256,24 @@ public async Task GetDocumentAsync( string fileId, CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(transactionId); + transactionId.ThrowIfNullOrEmpty(nameof(transactionId)); if (string.IsNullOrWhiteSpace(transactionId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); + throw new ArgumentException( + "Cannot be empty or contain only whitespaces.", + nameof(transactionId)); } - ArgumentNullException.ThrowIfNull(fileId); + fileId.ThrowIfNullOrEmpty(nameof(fileId)); if (string.IsNullOrWhiteSpace(fileId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(fileId)); + throw new ArgumentException( + "Cannot be empty or contain only whitespaces.", + nameof(fileId)); } var result = await client - .GetStreamAsync( - "transaction".JoinPaths(transactionId, "file", fileId)) + .GetStreamAsync("transaction".JoinPaths(transactionId, "file", fileId)) .ConfigureAwait(false); return result; diff --git a/src/SignhostAPIClient/Rest/SignHostApiClientSettings.cs b/src/SignhostAPIClient/Rest/SignHostApiClientSettings.cs index 15cd1df7..9512f2b8 100644 --- a/src/SignhostAPIClient/Rest/SignHostApiClientSettings.cs +++ b/src/SignhostAPIClient/Rest/SignHostApiClientSettings.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Signhost.APIClient.Rest; @@ -9,12 +9,17 @@ public class SignhostApiClientSettings public SignhostApiClientSettings(string appkey, string userToken) { + appkey.ThrowIfNullOrEmpty(nameof(appkey)); + userToken.ThrowIfNullOrEmpty(nameof(userToken)); + APPKey = appkey; UserToken = userToken; } public SignhostApiClientSettings(string appkey) { + appkey.ThrowIfNullOrEmpty(nameof(appkey)); + APPKey = appkey; } diff --git a/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs b/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs index aae80710..4a935e4a 100644 --- a/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs +++ b/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs @@ -28,7 +28,9 @@ public class SignhostApiReceiver /// public SignhostApiReceiver(SignhostApiReceiverSettings receiverSettings) { - this.settings = receiverSettings; + receiverSettings.ThrowIfNullOrEmpty(nameof(receiverSettings)); + + settings = receiverSettings; } /// @@ -37,6 +39,9 @@ public bool IsPostbackChecksumValid( string body, [NotNullWhen(true)] out Transaction? postbackTransaction) { + headers.ThrowIfNullOrEmpty(nameof(headers)); + body.ThrowIfNullOrEmpty(nameof(body)); + postbackTransaction = null; var postback = DeserializeToPostbackTransaction(body); if (postback is null) { @@ -50,35 +55,27 @@ public bool IsPostbackChecksumValid( if (parametersAreValid) { calculatedChecksum = CalculateChecksumFromPostback(postback); postbackTransaction = postback; - } else { + } + else { return false; } return Equals(calculatedChecksum, postbackChecksum); } - private string CalculateChecksumFromPostback(PostbackTransaction postback) + private static PostbackTransaction? DeserializeToPostbackTransaction(string body) { - using (var sha1 = SHA1.Create()) { - var checksumBytes = sha1.ComputeHash(Encoding.UTF8.GetBytes( - $"{postback.Id}||{(int)postback.Status}|{settings.SharedSecret}")); - return BitConverter.ToString(checksumBytes) - .Replace("-", string.Empty) - .ToLower(); - } + return JsonSerializer.Deserialize( + body, + SignhostJsonSerializerOptions.Default); } - private PostbackTransaction? DeserializeToPostbackTransaction(string body) - { - return JsonSerializer.Deserialize(body, SignhostJsonSerializerOptions.Default); - } - - private string GetChecksumFromHeadersOrPostback( + private static string GetChecksumFromHeadersOrPostback( IDictionary headers, PostbackTransaction postback) { if ( - headers.TryGetValue("Checksum", out var postbackChecksumArray) && + headers.TryGetValue("Checksum", out string[]? postbackChecksumArray) && postbackChecksumArray is not null ) { return postbackChecksumArray.First(); @@ -88,8 +85,18 @@ postbackChecksumArray is not null } } - private bool HasValidChecksumProperties(string postbackChecksum, PostbackTransaction postback) + private static bool HasValidChecksumProperties(string postbackChecksum, PostbackTransaction postback) { return !string.IsNullOrWhiteSpace(postbackChecksum) && !string.IsNullOrWhiteSpace(postback.Id); } + + private string CalculateChecksumFromPostback(PostbackTransaction postback) + { + using var sha1 = SHA1.Create(); + byte[] checksumBytes = sha1.ComputeHash(Encoding.UTF8.GetBytes( + $"{postback.Id}||{(int)postback.Status}|{settings.SharedSecret}")); + return BitConverter.ToString(checksumBytes) + .Replace("-", string.Empty) + .ToLower(); + } } diff --git a/src/SignhostAPIClient/Rest/SignhostApiReceiverSettings.cs b/src/SignhostAPIClient/Rest/SignhostApiReceiverSettings.cs index c1b2bbce..a8f8f97d 100644 --- a/src/SignhostAPIClient/Rest/SignhostApiReceiverSettings.cs +++ b/src/SignhostAPIClient/Rest/SignhostApiReceiverSettings.cs @@ -1,4 +1,4 @@ -namespace Signhost.APIClient.Rest; +namespace Signhost.APIClient.Rest; /// /// Registers the necessary settings for the class. @@ -7,6 +7,8 @@ public class SignhostApiReceiverSettings { public SignhostApiReceiverSettings(string sharedsecret) { + sharedsecret.ThrowIfNullOrEmpty(nameof(sharedsecret)); + SharedSecret = sharedsecret; } diff --git a/src/SignhostAPIClient/Rest/SignhostJsonSerializerOptions.cs b/src/SignhostAPIClient/Rest/SignhostJsonSerializerOptions.cs index 22c34542..7c7040cf 100644 --- a/src/SignhostAPIClient/Rest/SignhostJsonSerializerOptions.cs +++ b/src/SignhostAPIClient/Rest/SignhostJsonSerializerOptions.cs @@ -11,12 +11,10 @@ public static class SignhostJsonSerializerOptions /// /// Gets the default JSON serializer options. /// - public static JsonSerializerOptions Default { get; } = new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - Converters = { - new JsonStringEnumConverter(), - }, - }; + public static JsonSerializerOptions Default { get; } = + new JsonSerializerOptions { + PropertyNameCaseInsensitive = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { new JsonStringEnumConverter() }, + }; } diff --git a/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs b/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs index 8867a492..09d2e9d7 100644 --- a/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs +++ b/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs @@ -23,6 +23,10 @@ public static StreamContent WithDigest( Stream fileStream, FileDigestOptions options) { + content.ThrowIfNullOrEmpty(nameof(content)); + fileStream.ThrowIfNullOrEmpty(nameof(fileStream)); + options.ThrowIfNullOrEmpty(nameof(options)); + if ( !options.UseFileDigesting || options.DigestHashAlgorithm == DigestHashAlgorithm.None @@ -50,7 +54,7 @@ private static string GetDigestHashAlgorithmName(FileDigestOptions options) DigestHashAlgorithm.SHA512 => "SHA-512", _ => throw new InvalidOperationException( - $"No hash algorithm name for '{options.DigestHashAlgorithm}'"), + $"No hash algorithm name for '{options.DigestHashAlgorithm}'."), }; } @@ -68,9 +72,8 @@ private static void SetHashValue( long position = fileStream.Position; - using (var algo = HashAlgorithmCreate(options)) { - options.DigestHashValue = algo.ComputeHash(fileStream); - } + using var algo = HashAlgorithmCreate(options); + options.DigestHashValue = algo.ComputeHash(fileStream); fileStream.Position = position; } @@ -87,7 +90,7 @@ private static HashAlgorithm HashAlgorithmCreate( DigestHashAlgorithm.SHA512 => SHA512.Create(), #endif _ => throw new InvalidOperationException( - $"No hash algorithm for '{options.DigestHashAlgorithm}'"), + $"No hash algorithm for '{options.DigestHashAlgorithm}'."), }; } } diff --git a/src/SignhostAPIClient/Rest/ThrowIfNullExtensions.cs b/src/SignhostAPIClient/Rest/ThrowIfNullExtensions.cs new file mode 100644 index 00000000..ea627ad8 --- /dev/null +++ b/src/SignhostAPIClient/Rest/ThrowIfNullExtensions.cs @@ -0,0 +1,26 @@ +using System; + +namespace Signhost.APIClient.Rest; + +/// +/// Polyfill extensions for ArgumentNullException.ThrowIfNull that works across all target frameworks. +/// +internal static class ThrowIfNullExtensions +{ + /// + /// Throws an if the specified argument is null. + /// + /// The value to check for null. + /// The name of the parameter. + /// is null. + internal static void ThrowIfNullOrEmpty(this object? value, string? paramName = null) + { + if (value is null) { + throw new ArgumentNullException(paramName); + } + + if (value is string str && string.IsNullOrWhiteSpace(str)) { + throw new ArgumentException("Argument cannot be empty or whitespace.", paramName); + } + } +} diff --git a/src/SignhostAPIClient/Rest/UriPathExtensions.cs b/src/SignhostAPIClient/Rest/UriPathExtensions.cs index 9f13c529..9b5639b7 100644 --- a/src/SignhostAPIClient/Rest/UriPathExtensions.cs +++ b/src/SignhostAPIClient/Rest/UriPathExtensions.cs @@ -5,13 +5,6 @@ namespace Signhost.APIClient.Rest; internal static class UriPathExtensions { - internal static UriBuilder AppendPathSegment(this string url) - { - var builder = new UriBuilder(url); - - return builder; - } - internal static Uri JoinPaths( this string url, params string[] segments) From b30ca540355926355c30f9ca243b054f8a421b72 Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Fri, 28 Nov 2025 11:05:53 +0100 Subject: [PATCH 21/22] Add integration test for complex file metadata --- .../TransactionTests.cs | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs b/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs index 99416388..1d39224e 100644 --- a/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs +++ b/src/SignhostAPIClient.IntegrationTests/TransactionTests.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Text.Json; using System.Threading.Tasks; using FluentAssertions; using Signhost.APIClient.Rest.DataObjects; @@ -255,6 +257,191 @@ await client.AddOrReplaceFileToTransactionAsync( finalReceiver.Reference.Should().Be(receiverReference); } + [Fact] + public async Task Given_complex_file_metadata_When_added_to_transaction_Then_all_properties_are_accepted() + { + // Arrange - Create a simple transaction + string testReference = $"MetadataTest-{DateTime.UtcNow:yyyyMMddHHmmss}"; + CreateTransactionRequest transaction = new() { + Reference = testReference, + SendEmailNotifications = false, + Signers = [ + new() { + Id = "signer1", + Email = "test@example.com", + SendSignRequest = false, + }, + new() { + Id = "signer2", + Email = "test2@example.com", + SendSignRequest = false, + } + ] + }; + + var createdTransaction = await client.CreateTransactionAsync(transaction); + createdTransaction.Should().NotBeNull(); + + // Arrange - Create a very complex FileMeta + FileMeta fileMeta = new() { + DisplayOrder = 1, + DisplayName = "Complex Test Document", + Description = "This is a test document with complex metadata", + SetParaph = true, + Signers = new Dictionary { + ["signer1"] = new() { + FormSets = ["FormSet1", "FormSet2"], + }, + ["signer2"] = new() { + FormSets = ["FormSet2", "FormSet3"], + }, + }, + FormSets = new Dictionary> { + ["FormSet1"] = new Dictionary { + // Test all FileFieldType enum values + ["SealField"] = new Field { + Type = FileFieldType.Seal, + Value = null, + Location = new Location { + Search = "seal_placeholder", + Occurence = 1, + PageNumber = 1, + }, + }, + ["SignatureField"] = new Field { + Type = FileFieldType.Signature, + Value = null, + Location = new Location { + Top = 100, + Left = 50, + Width = 200, + Height = 60, + PageNumber = 1, + }, + }, + ["CheckField"] = new Field { + Type = FileFieldType.Check, + Value = "I agree to the terms", + Location = new Location { + Search = "checkbox_location", + Occurence = 1, + }, + }, + }, + ["FormSet2"] = new Dictionary { + // Test different value types: string, number, boolean, null + ["RadioFieldString"] = new Field { + Type = FileFieldType.Radio, + Value = "Option A", + Location = new Location { + Top = 200, + Left = 100, + Right = 300, + Bottom = 220, + PageNumber = 2, + }, + }, + ["SingleLineFieldString"] = new Field { + Type = FileFieldType.SingleLine, + Value = "John Doe", + Location = new Location { + Search = "name_field", + Width = 150, + Height = 20, + } + }, + ["NumberFieldInteger"] = new Field { + Type = FileFieldType.Number, + Value = 42, + Location = new Location { + Top = 300, + Left = 50, + PageNumber = 2, + }, + }, + ["NumberFieldDecimal"] = new Field { + Type = FileFieldType.Number, + Value = 123.45, + Location = new Location { + Top = 320, + Left = 50, + Width = 100, + Height = 20, + PageNumber = 2, + }, + }, + ["DateField"] = new Field { + Type = FileFieldType.Date, + Value = "2025-11-28", + Location = new Location { + Search = "date_placeholder", + Occurence = 2, + PageNumber = 3, + }, + }, + }, + ["FormSet3"] = new Dictionary { + // Test boolean values and all Location properties + ["CheckFieldTrue"] = new Field { + Type = FileFieldType.Check, + Value = true, + Location = new Location { + Top = 400, + Right = 200, + Bottom = 420, + Left = 50, + PageNumber = 3, + }, + }, + ["CheckFieldFalse"] = new Field { + Type = FileFieldType.Check, + Value = false, + Location = new Location { + Top = 450, + Right = 200, + Bottom = 470, + Left = 50, + Width = 150, + Height = 20, + PageNumber = 3, + }, + }, + ["SingleLineFieldNull"] = new Field { + Type = FileFieldType.SingleLine, + Value = null, + Location = new Location { + Search = "optional_field", + Occurence = 1, + Top = 500, + Left = 50, + Width = 200, + Height = 25, + PageNumber = 4, + }, + }, + ["RadioFieldNumber"] = new Field { + Type = FileFieldType.Radio, + Value = 1, + Location = new Location { + PageNumber = 4, + Top = 550, + Left = 50, + }, + }, + }, + }, + }; + + // Act + Func act = () => client.AddOrReplaceFileMetaToTransactionAsync( + fileMeta, + createdTransaction.Id, + "test-document.pdf"); + + // Assert + await act.Should().NotThrowAsync(); + } + public void Dispose() { client?.Dispose(); From 42fa26660ed1f96af1292cb483e78ad7f4d0ab24 Mon Sep 17 00:00:00 2001 From: Anthony Timmers Date: Fri, 26 Sep 2025 17:46:11 +0200 Subject: [PATCH 22/22] Refactor ensure successful status code --- .../Rest/SignHostApiClient.cs | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/SignhostAPIClient/Rest/SignHostApiClient.cs b/src/SignhostAPIClient/Rest/SignHostApiClient.cs index 6361ec6f..eba12797 100644 --- a/src/SignhostAPIClient/Rest/SignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/SignHostApiClient.cs @@ -243,11 +243,20 @@ public async Task GetReceiptAsync( nameof(transactionId)); } - var result = await client - .GetStreamAsync("file".JoinPaths("receipt", transactionId)) + var response = await client + .GetAsync( + "file".JoinPaths("receipt", transactionId), + cancellationToken) + .EnsureSignhostSuccessStatusCodeAsync() .ConfigureAwait(false); - return result; + return await response.Content +#if NET8_0_OR_GREATER + .ReadAsStreamAsync(cancellationToken) +#else + .ReadAsStreamAsync() +#endif + .ConfigureAwait(false); } /// @@ -272,11 +281,20 @@ public async Task GetDocumentAsync( nameof(fileId)); } - var result = await client - .GetStreamAsync("transaction".JoinPaths(transactionId, "file", fileId)) + var response = await client + .GetAsync( + "transaction".JoinPaths(transactionId, "file", fileId), + cancellationToken) + .EnsureSignhostSuccessStatusCodeAsync() .ConfigureAwait(false); - return result; + return await response.Content +#if NET8_0_OR_GREATER + .ReadAsStreamAsync(cancellationToken) +#else + .ReadAsStreamAsync() +#endif + .ConfigureAwait(false); } ///