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/.github/workflows/publish_nuget_package.yml b/.github/workflows/publish_nuget_package.yml index dc459d1c..8ce91f8a 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/.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/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.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..92ca645a 100644 --- a/src/SignhostAPIClient.Tests/LevelEnumConverterTests.cs +++ b/src/SignhostAPIClient.Tests/LevelEnumConverterTests.cs @@ -1,79 +1,78 @@ using FluentAssertions; -using Newtonsoft.Json; +using System.Text.Json; using Signhost.APIClient.Rest.DataObjects; using System; using System.Collections; 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 = JsonConvert.DeserializeObject(json); + // 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 = JsonConvert.DeserializeObject(json); + // 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 = JsonConvert.DeserializeObject(json); + // 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 = JsonConvert.DeserializeObject(json); + // 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 a4724723..8d3314cd 100644 --- a/src/SignhostAPIClient.Tests/PostbackTests.cs +++ b/src/SignhostAPIClient.Tests/PostbackTests.cs @@ -1,119 +1,116 @@ 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 +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 = RequestBodies.MockPostbackValid; - var postbackTransaction = JsonConvert.DeserializeObject(json); - - 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.CancelledDateTime .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.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; - 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/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 cc06f7b3..d7aa909c 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 @@ -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/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.Tests/SignhostApiClientTests.cs b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs index 041a40f4..80e0fbb7 100644 --- a/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs +++ b/src/SignhostAPIClient.Tests/SignhostApiClientTests.cs @@ -8,662 +8,662 @@ using System.Collections.Generic; using RichardSzalay.MockHttp; 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(RequestBodies.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(APIResponses.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(APIResponses.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(APIResponses.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(APIResponses.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(APIResponses.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" } + """)); + + 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_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(APIResponses.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(APIResponses.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 = DigestHashAlgorithm.SHA512 + } + }); } - [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-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 } + } + }); } - [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(RequestBodies.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.CancelledDateTime.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"); } + }); + + 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") + }); + } - return request - .WithHeaders("Application", "APPKey AppKey") - .WithHeaders("X-Custom", "test"); - } + mockHttp.VerifyNoOutstandingExpectation(); + } - var mockHttp = new MockHttpMessageHandler(); - AddHeaders(mockHttp.Expect(HttpMethod.Post, "http://localhost/api/transaction")) - .Respond(new StringContent(RequestBodies.TransactionSingleSignerJson)); - AddHeaders(mockHttp.Expect(HttpMethod.Put, "http://localhost/api/transaction/*/file/somefileid")) - .Respond(HttpStatusCode.Accepted, new StringContent(RequestBodies.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); + [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(); - mockHttp.VerifyNoOutstandingRequest(); + return request + .WithHeaders("Application", "APPKey AppKey") + .WithHeaders("X-Custom", "test"); } - [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"); + 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); } - [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(); + mockHttp.VerifyNoOutstandingExpectation(); + mockHttp.VerifyNoOutstandingRequest(); + } - using (var httpClient = mockHttp.ToHttpClient()) - { - var signhostApiClient = new SignHostApiClient(settings, httpClient); + [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)); - var result = await signhostApiClient.GetTransactionAsync("c487be92-0255-40c7-bd7d-20805a65e7d9"); + using (var httpClient = mockHttp.ToHttpClient()) + { + var signhostApiClient = new SignhostApiClient(settings, httpClient); - result.Signers.Should().BeEmpty(); - result.Receivers.Should().BeEmpty(); - result.Files.Should().BeEmpty(); - } - } + var result = await signhostApiClient.GetTransactionAsync("c487be92-0255-40c7-bd7d-20805a65e7d9"); - public class CustomVerification - : IVerification - { - public string Type => "CustomVerificationType"; + 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 79099cc4..4bab87eb 100644 --- a/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs +++ b/src/SignhostAPIClient.Tests/SignhostApiReceiverTests.cs @@ -1,84 +1,78 @@ -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 +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 = RequestBodies.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 = RequestBodies.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 = RequestBodies.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 = RequestBodies.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.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.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/Rest/ApiResponse.cs b/src/SignhostAPIClient/Rest/ApiResponse.cs index f6c6c128..bb984c42 100644 --- a/src/SignhostAPIClient/Rest/ApiResponse.cs +++ b/src/SignhostAPIClient/Rest/ApiResponse.cs @@ -1,32 +1,28 @@ -using System; -using System.Collections.Generic; -using System.Net; +using System.Net; using System.Net.Http; -using System.Text; -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); - } + public void EnsureAvailableStatusCode() + { + if (HttpStatusCode == HttpStatusCode.Gone) { + throw new ErrorHandling.GoneException( + httpResponse.ReasonPhrase, + Value); } } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Activity.cs b/src/SignhostAPIClient/Rest/DataObjects/Activity.cs index dccf8caa..a8d82fb9 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Activity.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Activity.cs @@ -1,15 +1,14 @@ using System; -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; } - 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 e954356a..ecd9aad2 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/ActivityType.cs @@ -1,139 +1,134 @@ -using System; -using System.Collections.Generic; -using System.Text; +namespace Signhost.APIClient.Rest.DataObjects; -namespace Signhost.APIClient.Rest.DataObjects +/// +/// type. +/// +public enum ActivityType { /// - /// type. - /// - 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 18c1cfd0..6a045ce3 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ConsentVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/ConsentVerification.cs @@ -1,18 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Text; +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 - { - /// - /// Gets the . - /// - public string Type { get; } = "Consent"; - } } 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 b4034816..798d37c0 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/DigidVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/DigidVerification.cs @@ -1,10 +1,7 @@ -namespace Signhost.APIClient.Rest.DataObjects -{ - public class DigidVerification - : IVerification - { - public string Type => "DigiD"; +namespace Signhost.APIClient.Rest.DataObjects; - public string Bsn { get; set; } - } +public class DigidVerification + : IVerification +{ + public string Bsn { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/EidasLoginVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/EidasLoginVerification.cs index 72deeac5..3d54b0ef 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/EidasLoginVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/EidasLoginVerification.cs @@ -1,47 +1,44 @@ using System; using System.Collections.Generic; +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 the . - /// - public string Type { get; } = "eIDAS Login"; - - /// - /// Gets or sets the uid. - /// - public string Uid { get; set; } + public string Uid { get; set; } - /// - /// Gets or sets the level. - /// - 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 2c2482bc..1c83d030 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Field.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Field.cs @@ -1,11 +1,10 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class Field { - public class Field - { - public string Type { get; set; } + public string Type { get; set; } - public string Value { get; set; } + 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 4c0f2eba..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 Newtonsoft.Json; +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. - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - 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 ea2f0eb8..b1fb67f3 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/IPAddressVerification.cs @@ -1,33 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Newtonsoft.Json; +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 - { - /// - /// 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. - /// - 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 fe76ce2c..6a18aa03 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/IVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/IVerification.cs @@ -1,11 +1,20 @@ -using Newtonsoft.Json; -using Signhost.APIClient.Rest.JsonConverters; +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(ItsmeSignVerification), "itsme sign")] +[JsonDerivedType(typeof(PhoneNumberVerification), "PhoneNumber")] +[JsonDerivedType(typeof(ScribbleVerification), "Scribble")] +[JsonDerivedType(typeof(SigningCertificateVerification), "SigningCertificate")] +[JsonDerivedType(typeof(SurfnetVerification), "SURFnet")] +public interface IVerification { - [JsonConverter(typeof(JsonVerificationConverter))] - public interface IVerification - { - string Type { get; } - } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/IdealVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/IdealVerification.cs index 459e159b..55f1bc95 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/IdealVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/IdealVerification.cs @@ -1,14 +1,11 @@ -namespace Signhost.APIClient.Rest.DataObjects -{ - public class IdealVerification - : IVerification - { - public string Type => "iDeal"; +namespace Signhost.APIClient.Rest.DataObjects; - public string Iban { get; set; } +public class IdealVerification + : IVerification +{ + 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 684438c6..150f398b 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/IdinVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/IdinVerification.cs @@ -1,21 +1,18 @@ using System; using System.Collections.Generic; -namespace Signhost.APIClient.Rest.DataObjects -{ - public class IdinVerification - : IVerification - { - public string Type { get; } = "iDIN"; +namespace Signhost.APIClient.Rest.DataObjects; - public string AccountHolderName { get; set; } +public class IdinVerification + : IVerification +{ + 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 c2c069e6..914665e4 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ItsmeIdentificationVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/ItsmeIdentificationVerification.cs @@ -1,19 +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 the . - /// - public string Type => "itsme Identification"; - - /// - /// Gets or sets the phonenumber. - /// - public string PhoneNumber { get; set; } - } + public string PhoneNumber { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/ItsmeSignVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/ItsmeSignVerification.cs index 20699888..66941355 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ItsmeSignVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/ItsmeSignVerification.cs @@ -1,14 +1,9 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +/// +/// Verification object for itsme sign. +/// +public class ItsmeSignVerification + : IVerification { - /// - /// 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..a96cd7f5 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Level.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Level.cs @@ -1,32 +1,27 @@ -using Newtonsoft.Json; -using Signhost.APIClient.Rest.JsonConverters; +namespace Signhost.APIClient.Rest.DataObjects; -namespace Signhost.APIClient.Rest.DataObjects +/// +/// Level of Assurance. +/// +public enum Level { /// - /// Level of Assurance. + /// Unknown. /// - [JsonConverter(typeof(LevelEnumConverter))] - 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/PhoneNumberVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/PhoneNumberVerification.cs index 8503fbfb..85f1fe25 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/PhoneNumberVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/PhoneNumberVerification.cs @@ -1,10 +1,7 @@ -namespace Signhost.APIClient.Rest.DataObjects -{ - public class PhoneNumberVerification - : IVerification - { - public string Type => "PhoneNumber"; +namespace Signhost.APIClient.Rest.DataObjects; - public string Number { get; set; } - } +public class PhoneNumberVerification + : IVerification +{ + public string Number { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs b/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs index 4c5db856..cffc6ebb 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/PostbackTransaction.cs @@ -1,27 +1,10 @@ -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) - { - } +namespace Signhost.APIClient.Rest.DataObjects; - [JsonProperty("Checksum")] - public string Checksum { get; set; } - } +public class PostbackTransaction + : Transaction +{ + [JsonPropertyName("Checksum")] + public string Checksum { get; set; } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Receiver.cs b/src/SignhostAPIClient/Rest/DataObjects/Receiver.cs index 8fc286e2..cf8a3642 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Receiver.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Receiver.cs @@ -1,35 +1,34 @@ using System.Collections.Generic; -using Newtonsoft.Json; +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 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 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 64f18a75..5556f0b5 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/ScribbleVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/ScribbleVerification.cs @@ -1,14 +1,11 @@ -namespace Signhost.APIClient.Rest.DataObjects -{ - public class ScribbleVerification - : IVerification - { - public string Type => "Scribble"; +namespace Signhost.APIClient.Rest.DataObjects; - public bool RequireHandsignature { get; set; } +public class ScribbleVerification + : IVerification +{ + 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 77cada56..e9a4517d 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Signer.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Signer.cs @@ -1,72 +1,71 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json; +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 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/SigningCertificateVerification.cs b/src/SignhostAPIClient/Rest/DataObjects/SigningCertificateVerification.cs index 98673fd3..f9420a4f 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/SigningCertificateVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/SigningCertificateVerification.cs @@ -1,13 +1,10 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +/// +/// Represents a verification method using a signers signing certificate +/// for example a qualified certificate. +/// +public class SigningCertificateVerification + : IVerification { - /// - /// 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 466551c8..72abf3cf 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/SurfnetVerification.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/SurfnetVerification.cs @@ -1,8 +1,6 @@ -namespace Signhost.APIClient.Rest.DataObjects +namespace Signhost.APIClient.Rest.DataObjects; + +public class SurfnetVerification + : IVerification { - public class SurfnetVerification - : IVerification - { - public string Type => "SURFnet"; - } } diff --git a/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs b/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs index 534d31e6..7ac62205 100644 --- a/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs +++ b/src/SignhostAPIClient/Rest/DataObjects/Transaction.cs @@ -1,68 +1,52 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json; -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; - } +namespace Signhost.APIClient.Rest.DataObjects; - public string Id { get; set; } +public class Transaction +{ + public string Id { get; set; } - /// - /// Gets the when the was created. - /// - public DateTimeOffset? CreatedDateTime { get; } + /// + /// 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? CancelledDateTime { get; } + /// + /// 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; } + /// + /// Gets the cancellation reason when the was cancelled. + /// + public string CancellationReason { get; set; } - public IReadOnlyDictionary Files { get; private set; } + 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/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/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/ErrorHandling/BadAuthorizationException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs index 04bc32b1..18260ac2 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/BadAuthorizationException.cs @@ -1,41 +1,35 @@ 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 +namespace Signhost.APIClient.Rest.ErrorHandling; + +[Serializable] +public class BadAuthorizationException + : SignhostRestApiClientException { - [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 8fb0d528..d449b02b 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/BadRequestException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/BadRequestException.cs @@ -1,37 +1,35 @@ using System; -using System.Runtime.Serialization; -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 ca6fb125..b7db21ab 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/DefaultSignhostException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/DefaultSignhostException.cs @@ -1,31 +1,29 @@ using System; -using System.Runtime.Serialization; -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 b902fe62..5aad3757 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/GoneException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/GoneException.cs @@ -1,63 +1,61 @@ using System; -using System.Runtime.Serialization; -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 745b17c9..35edbd3e 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/HttpResponseMessageErrorHandlingExtensions.cs @@ -2,102 +2,104 @@ 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 +namespace Signhost.APIClient.Rest.ErrorHandling; + +/// +/// Error handling around s. +/// +public static class HttpResponseMessageErrorHandlingExtensions { /// - /// 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) { - /// - /// 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; - } + var response = await responseTask.ConfigureAwait(false); - if (expectedStatusCodes.Contains(response.StatusCode)) { - return response; - } + if (response.IsSuccessStatusCode) { + return response; + } - string errorType = string.Empty; - string errorMessage = "Unknown Signhost error"; + if (expectedStatusCodes.Contains(response.StatusCode)) { + return response; + } - if (response.Content != null) { - string responsejson = await response.Content.ReadAsStringAsync() - .ConfigureAwait(false); + string errorType = string.Empty; + string errorMessage = "Unknown Signhost error"; - var error = JsonConvert.DeserializeAnonymousType( - responsejson, - new { - Type = string.Empty, - Message = string.Empty, - }); + if (response.Content != null) { + string responsejson = await response.Content.ReadAsStringAsync() + .ConfigureAwait(false); - errorType = error.Type; - errorMessage = error.Message; - } + var error = JsonSerializer.Deserialize(responsejson); - switch (response.StatusCode) { - case HttpStatusCode.Unauthorized: - throw new System.UnauthorizedAccessException( - errorMessage); - case HttpStatusCode.BadRequest: - throw new BadRequestException( - errorMessage); - case HttpStatusCode.PaymentRequired - when errorType == "https://api.signhost.com/problem/subscription/out-of-credits": - if (string.IsNullOrEmpty(errorMessage)) { - errorMessage = "The credit bundle has been exceeded."; - } + errorType = error?.Type ?? string.Empty; + errorMessage = error?.Message ?? "Unknown Signhost error"; + } - throw new OutOfCreditsException( - errorMessage); - case HttpStatusCode.NotFound: - throw new NotFoundException( - errorMessage); - case HttpStatusCode.InternalServerError: - throw new InternalServerErrorException( - errorMessage, response.Headers.RetryAfter); - default: - throw new SignhostRestApiClientException( - errorMessage); - } + switch (response.StatusCode) { + case HttpStatusCode.Unauthorized: + throw new System.UnauthorizedAccessException( + errorMessage); + case HttpStatusCode.BadRequest: + throw new BadRequestException( + errorMessage); + case HttpStatusCode.PaymentRequired + when errorType == "https://api.signhost.com/problem/subscription/out-of-credits": + if (string.IsNullOrEmpty(errorMessage)) { + errorMessage = "The credit bundle has been exceeded."; + } - System.Diagnostics.Debug.Fail("Should not be reached"); + throw new OutOfCreditsException( + errorMessage); + case HttpStatusCode.NotFound: + throw new NotFoundException( + errorMessage); + case HttpStatusCode.InternalServerError: + throw new InternalServerErrorException( + errorMessage, response.Headers.RetryAfter); + default: + throw new SignhostRestApiClientException( + errorMessage); } } + + 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 927c49f2..265489f0 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/InternalServerErrorException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/InternalServerErrorException.cs @@ -1,49 +1,47 @@ using System; using System.Net.Http.Headers; -using System.Runtime.Serialization; -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 ff21de45..0b61c9e3 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/NotFoundException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/NotFoundException.cs @@ -1,35 +1,33 @@ using System; -using System.Runtime.Serialization; -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/SignhostException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/SignhostException.cs index fd06b202..b24db3f6 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/SignhostException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/SignhostException.cs @@ -1,36 +1,34 @@ using System; -using System.Runtime.Serialization; -namespace Signhost.APIClient.Rest.ErrorHandling +namespace Signhost.APIClient.Rest.ErrorHandling; + +[Serializable] +[Obsolete("Unused will be removed")] +public class SignhostException + : SignhostRestApiClientException { - [Serializable] - [Obsolete("Unused will be removed")] - public class SignhostException - : SignhostRestApiClientException + public SignhostException() + : base() { - public SignhostException() - : base() - { - } + } - public SignhostException(string message) - : base(message) - { - } + public SignhostException(string message) + : base(message) + { + } - public SignhostException(string message, Exception innerException) - : base(message, innerException) - { - HelpLink = "https://api.signhost.com/Help"; - } + 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 + protected SignhostException( + SerializationInfo info, + StreamingContext context) + : base(info, context) + { } +#endif } diff --git a/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs b/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs index 600a3c53..f7c0b0d9 100644 --- a/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs +++ b/src/SignhostAPIClient/Rest/ErrorHandling/SignhostRestApiClientException.cs @@ -1,37 +1,35 @@ using System; -using System.Runtime.Serialization; -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( - SerializationInfo info, - StreamingContext context) - : base(info, context) - { - } -#endif + protected SignhostRestApiClientException( + SerializationInfo info, + StreamingContext context) + : base(info, context) + { } +#endif } diff --git a/src/SignhostAPIClient/Rest/FileDigestOptions.cs b/src/SignhostAPIClient/Rest/FileDigestOptions.cs index 02d42487..c5580a2a 100644 --- a/src/SignhostAPIClient/Rest/FileDigestOptions.cs +++ b/src/SignhostAPIClient/Rest/FileDigestOptions.cs @@ -1,31 +1,26 @@ -using System; -using System.IO; -using System.Security.Cryptography; +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 DigestHashAlgorithm DigestHashAlgorithm { get; set; } = DigestHashAlgorithm.SHA256; - /// - /// 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 e205e41a..3dfa535f 100644 --- a/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs +++ b/src/SignhostAPIClient/Rest/HttpContentJsonExtensions.cs @@ -1,31 +1,30 @@ using System.Net.Http; +using System.Text.Json; using System.Threading.Tasks; -using Newtonsoft.Json; -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 JsonConvert.DeserializeObject(json); + 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 f4546fd8..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 439dc127..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 ac8441a7..ba4eb961 100644 --- a/src/SignhostAPIClient/Rest/ISignhostApiReceiver.cs +++ b/src/SignhostAPIClient/Rest/ISignhostApiReceiver.cs @@ -1,26 +1,22 @@ -using System; using System.Collections.Generic; -using System.Threading.Tasks; -using Signhost.APIClient.Rest; 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 54e72b4c..cb31cea6 100644 --- a/src/SignhostAPIClient/Rest/JsonContent.cs +++ b/src/SignhostAPIClient/Rest/JsonContent.cs @@ -1,46 +1,45 @@ using System.Net.Http; using System.Net.Http.Headers; -using Newtonsoft.Json; +using System.Text.Json; -namespace Signhost.APIClient.Rest +namespace Signhost.APIClient.Rest; + +/// +/// See the helper class. +/// +internal static class JsonContent { /// - /// 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 JsonConvert.SerializeObject(value); - } + private static string ToJson(T 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 cc3f3e53..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 - { -#if TYPEINFO - public override bool CanConvert(Type objectType) - => typeof(T).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo()); -#else - public override bool CanConvert(Type objectType) - => typeof(T).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 3019809e..00000000 --- a/src/SignhostAPIClient/Rest/JsonConverters/JsonVerificationConverter.cs +++ /dev/null @@ -1,64 +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 - { - private static readonly IDictionary VerificationTypes = - 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] = typeof(T).GetTypeInfo(); - } - - protected override IVerification Create( - Type objectType, - JObject jsonObject) - { - var typeName = jsonObject["Type"]?.ToString(); - - if (VerificationTypes.TryGetValue(typeName, out var verificationType)) { - return (IVerification)Activator.CreateInstance(verificationType.AsType()); - } - - return new UnknownVerification(); - } - - private static IDictionary 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); - } - } -} diff --git a/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs b/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs index c088635a..06b9c0b8 100644 --- a/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs +++ b/src/SignhostAPIClient/Rest/JsonConverters/LevelEnumConverter.cs @@ -1,57 +1,55 @@ 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 +namespace Signhost.APIClient.Rest.JsonConverters; + +/// +/// JSON converter factory for converting the enum. +/// Invalid values are mapped to . +/// +internal class LevelEnumConverter + : JsonConverter { - /// - /// JSON converter 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 bool CanWrite => false; - - /// - public override bool CanConvert(Type objectType) - => IsLevelEnum(GetUnderlyingType(objectType)); - - /// - public override object ReadJson( - JsonReader reader, - Type objectType, - object existingValue, - JsonSerializer serializer) - { - var value = reader.Value as string; - - if (value != null) { - if (Enum.TryParse(value, out Level level)) { - return level; - } + if (reader.TokenType == JsonTokenType.Null) { + return null; + } - return Level.Unknown; + if (reader.TokenType == JsonTokenType.String) { + var value = reader.GetString() ?? string.Empty; + if (Enum.TryParse(value, out var level)) { + return level; } - return null; + return Level.Unknown; } - /// - 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) - => type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) - ? Nullable.GetUnderlyingType(type) - : type; + return Level.Unknown; + } - private static bool IsLevelEnum(Type type) - => type.GetTypeInfo().IsEnum && 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 093632b8..4c7f698b 100644 --- a/src/SignhostAPIClient/Rest/SignHostApiClient.cs +++ b/src/SignhostAPIClient/Rest/SignHostApiClient.cs @@ -9,482 +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) - .GetTypeInfo() - .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); - } - - this.client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse($"application/vnd.signhost.{ApiVersion}+json")); - settings.AddHeader?.Invoke(this.client.DefaultRequestHeaders.Add); + /// + /// 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); } - private string ApplicationHeader - => $"APPKey {settings.APPKey}"; + this.client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue.Parse($"application/vnd.signhost.{ApiVersion}+json")); + settings.AddHeader?.Invoke(this.client.DefaultRequestHeaders.Add); + } - private string AuthorizationHeader - => $"APIKey {settings.UserToken}"; + private string ApplicationHeader + => $"APPKey {settings.APPKey}"; - /// - /// Globally register an additional verification type. - /// - /// to - public static void RegisterVerification() - where T : IVerification - { - JsonConverters.JsonVerificationConverter.RegisterVerification(); + 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)); } - /// - public async Task CreateTransactionAsync( - Transaction transaction) - => await CreateTransactionAsync(transaction, default) - .ConfigureAwait(false); + var result = await client + .PostAsync( + "transaction", + JsonContent.From(transaction), + cancellationToken) + .EnsureSignhostSuccessStatusCodeAsync() + .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); + return await result.Content.FromJsonAsync() + .ConfigureAwait(false); + } - return await result.Content.FromJsonAsync() - .ConfigureAwait(false); + /// + public async Task> GetTransactionResponseAsync( + string transactionId) + => await GetTransactionResponseAsync(transactionId, default) + .ConfigureAwait(false); + + /// + public async Task> GetTransactionResponseAsync( + string transactionId, + CancellationToken cancellationToken = default) + { + if (transactionId == null) { + throw new ArgumentNullException(nameof(transactionId)); } - /// - public async Task> GetTransactionResponseAsync( - string transactionId) - => await GetTransactionResponseAsync(transactionId, default) - .ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(transactionId)) { + throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); + } - /// - public async Task> GetTransactionResponseAsync( - string transactionId, - CancellationToken cancellationToken = default) - { - if (transactionId == null) { - throw new ArgumentNullException(nameof(transactionId)); - } + var result = await client + .GetAsync( + "transaction".JoinPaths(transactionId), + cancellationToken) + .EnsureSignhostSuccessStatusCodeAsync(HttpStatusCode.Gone) + .ConfigureAwait(false); + var transaction = await result.Content.FromJsonAsync() + .ConfigureAwait(false); - if (string.IsNullOrWhiteSpace(transactionId)) { - throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); - } + return new ApiResponse(result, transaction); + } - 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) + => await GetTransactionAsync(transactionId, default) + .ConfigureAwait(false); - return new ApiResponse(result, transaction); + /// + 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)); } - /// - public async Task GetTransactionAsync(string transactionId) - => await GetTransactionAsync(transactionId, default) - .ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(transactionId)) { + throw new ArgumentException("Cannot be empty or contain only whitespaces.", nameof(transactionId)); + } - /// - public async Task GetTransactionAsync( - string transactionId, - CancellationToken cancellationToken = default) - { - var response = await GetTransactionResponseAsync( - 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) - .ConfigureAwait(false); + .EnsureSignhostSuccessStatusCodeAsync() + .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, + string transactionId, + string fileId, + CancellationToken cancellationToken = default) + { + if (fileMeta == null) { + throw new ArgumentNullException("fileMeta"); + } - response.EnsureAvailableStatusCode(); + if (transactionId == null) { + throw new ArgumentNullException(nameof(transactionId)); + } - return response.Value; + if (string.IsNullOrWhiteSpace(transactionId)) { + throw new ArgumentException("Cannot be empty or contain only whitespaces.", 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 (fileId == null) { + throw new ArgumentNullException(nameof(fileId)); } - /// - 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 (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(); + } + + 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 StartTransactionAsync( - string transactionId, - CancellationToken cancellationToken = default) + /// + 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 f93a6c9b..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 a3bfde6a..d4fb3bfa 100644 --- a/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs +++ b/src/SignhostAPIClient/Rest/SignhostApiReceiver.cs @@ -3,88 +3,89 @@ 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; -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 . - /// - /// - 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 JsonConvert.DeserializeObject(body); + 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 cb8fd629..c1b2bbce 100644 --- a/src/SignhostAPIClient/Rest/SignhostApiReceiverSettings.cs +++ b/src/SignhostAPIClient/Rest/SignhostApiReceiverSettings.cs @@ -1,21 +1,18 @@ -using System; +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 new file mode 100644 index 00000000..22c34542 --- /dev/null +++ b/src/SignhostAPIClient/Rest/SignhostJsonSerializerOptions.cs @@ -0,0 +1,22 @@ +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/Rest/StreamContentDigestOptionsExtensions.cs b/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs index 2aad9008..32e27d47 100644 --- a/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs +++ b/src/SignhostAPIClient/Rest/StreamContentDigestOptionsExtensions.cs @@ -3,105 +3,87 @@ 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 == DigestHashAlgorithm.None + ) { 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", $"{GetDigestHashAlgorithmName(options)}={base64Digest}"); - using (var algo = HashAlgorithmCreate(options)) { - options.DigestHashValue = algo.ComputeHash(fileStream); - } + return content; + } - fileStream.Position = position; - } + 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 HashAlgorithm HashAlgorithmCreate( - FileDigestOptions options) - { - string algorithmName = options.DigestHashAlgorithm; - HashAlgorithm algorithm = null; + private static void SetHashValue( + Stream fileStream, + FileDigestOptions options) + { + if (options.DigestHashValue != null) { + return; + } -#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 - algorithm = HashAlgorithm.Create(algorithmName); -#endif - if (algorithm == null && options.DigestHashValue == null) { - algorithm = DefaultHashAlgorithm(); - options.DigestHashAlgorithm = algorithm.GetType().Name; - } + if (!fileStream.CanSeek) { + return; + } - if (algorithm == null) { - throw new InvalidOperationException($"No hash algorithm for '{algorithmName}'"); - } + long position = fileStream.Position; - return algorithm; + using (var algo = HashAlgorithmCreate(options)) { + options.DigestHashValue = algo.ComputeHash(fileStream); } - private static HashAlgorithm DefaultHashAlgorithm() => -#if NETSTANDARD1_4 || NETSTANDARD2_0 - SHA256.Create(); + fileStream.Position = position; + } + + private static HashAlgorithm HashAlgorithmCreate( + FileDigestOptions options) + { + return options.DigestHashAlgorithm switch { +#if NET462 + DigestHashAlgorithm.SHA256 => HashAlgorithm.Create("SHA256"), + DigestHashAlgorithm.SHA512 => HashAlgorithm.Create("SHA512"), #else - HashAlgorithm.Create(); + DigestHashAlgorithm.SHA256 => SHA256.Create(), + DigestHashAlgorithm.SHA512 => SHA512.Create(), #endif + _ => throw new InvalidOperationException( + $"No hash algorithm for '{options.DigestHashAlgorithm}'"), + }; } } 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); } } diff --git a/src/SignhostAPIClient/SignhostAPIClient.csproj b/src/SignhostAPIClient/SignhostAPIClient.csproj index f8acfff0..7309d334 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 @@ -44,7 +42,7 @@ - + @@ -53,9 +51,4 @@ - - - - - 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 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 -} 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 @@ + + + +