diff --git a/CHANGELOG.md b/CHANGELOG.md index 223c641c..a127498f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com) and this project adheres to [Semantic Versioning](http://semver.org). ## [Unreleased] - TBD - +### Added +- Document Groups: + - Document Group Invites: create, get status, cancel, resend, reassign signer, and list pending invites for a document group signing workflow + - Document Group Embedded: create/cancel embedded invites, generate embedded invite links, and embedded editor/sending links for a document group + - Document Group Recipients: get and update recipients, expiration, reminder and signing order settings for a document group ## [1.5.0] - 2025-02-05 ### Added diff --git a/LICENSE b/LICENSE index d81f9f85..6e452d38 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 airSlate Inc. +Copyright (c) 2003-present SignNow Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index d8f13945..9a548a22 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# signNow.NET +# SignNow API .NET SDK [![Build status][actions build badge]][actions build link] [![Codecov][codecov badge]][codecov link] @@ -6,9 +6,9 @@ [![NuGet Downloads][nuget downloads badge]][nuget downloads link] [![License][license badge]](LICENSE) -## About signNow +## About SignNow -signNow.Net is the official .NET 4.6.2+ and .NET Standard class library for the signNow API. signNow allows you to embed legally-binding e-signatures into your app, CRM or cloud storage. Send documents for signature directly from your website. Invite multiple signers to finalize contracts. Track status of your requests and download signed copies automatically. +SignNow.Net is the official .NET 4.6.2+ and .NET Standard class library for the SignNow API. SignNow allows you to embed legally-binding e-signatures into your app, CRM or cloud storage. Send documents for signature directly from your website. Invite multiple signers to finalize contracts. Track status of your requests and download signed copies automatically. Get your account at @@ -32,11 +32,11 @@ Get your account at - [Get modified documents][get_modified_docs example] - [Get user documents][get_user_docs example] - [Document](#document) - - [Upload a document to signNow][upload_document example] + - [Upload a document to SignNow][upload_document example] - [Upload a document & Extract Fields][upload_doc_extract example] - - [Download a document from signNow][download_signed_doc example] + - [Download a document from SignNow][download_signed_doc example] - [Retrieve a document resource][get_document example] - - [Merge two or more signNow documents into one][merge_documents example] + - [Merge two or more SignNow documents into one][merge_documents example] - [Create a signing link to the document for signature][create_sign_lnk example] - [Create a freeform invite to the document for signature][create_ff_invite example] - [Create a role-based invite to the document for signature][create_rb_invite example] @@ -71,7 +71,7 @@ Get your account at ## Get started -To start using the API you will need an API key. You can get one here . For a full list of accepted parameters, refer to the signNow REST Endpoints API guide: [signNow API Reference][api reference link]. +To start using the API you will need an API key. You can get one here . For a full list of accepted parameters, refer to the SignNow REST Endpoints API guide: [SignNow API Reference][api reference link]. #### API and Application @@ -108,7 +108,7 @@ Install-Package SignNow.Net -Version ## Documentation -Read about the available signNow features in [signNow API Docs][api docs link]. +Read about the available SignNow features in [SignNow API Docs][api docs link]. ## Features @@ -124,7 +124,7 @@ public static class AuthenticationExamples /// /// An example of obtaining an access token via OAuth 2.0 service. /// - /// signNow API base URL. Sandbox: "https://api-eval.signnow.com", Production: "https://api.signnow.com" + /// SignNow API base URL. Sandbox: "https://api-eval.signnow.com", Production: "https://api.signnow.com" /// with Application Client ID and Client Secret /// with User email and User password public static async Task RequestAccessToken(Uri apiBase, CredentialModel clientInfo, CredentialModel userCredentials) @@ -191,15 +191,15 @@ More examples: [Create User][create_user example], [Retrieve User information][g ## Document -### Upload a document to signNow +### Upload a document to SignNow -All the features in signNow require a `document_id`. Once you upload a document to signNow, you get the `document_id` from a successful response. +All the features in SignNow require a `document_id`. Once you upload a document to SignNow, you get the `document_id` from a successful response. ```csharp public static class DocumentExamples { /// - /// Uploads a PDF document to signNow and returns SignNowDocument object. + /// Uploads a PDF document to SignNow and returns SignNowDocument object. /// /// Full qualified path to your PDF file. /// Access token @@ -225,11 +225,11 @@ public static class DocumentExamples More examples: [Upload document][upload_document example], [Upload document with field extract][upload_doc_extract example], [Upload document with complex tags][upload_document_complex_tags] -### Download a document from signNow +### Download a document from SignNow Choose the type of download for your document: -- `PdfOriginal` - download a document in a state it's been when uploaded to signNow, before any changes +- `PdfOriginal` - download a document in a state it's been when uploaded to SignNow, before any changes - `PdfCollapsed` - download a document in PDF file format - `ZipCollapsed` - download a document in ZIP archive - `PdfWithHistory` - download a document with its history, a full log of changes on a separate page. @@ -258,9 +258,9 @@ public static class DocumentExamples More examples: [Download signed document][download_signed_doc example] -### Merge two or more signNow documents into one +### Merge two or more SignNow documents into one -Merges two or more signNow documents into one single PDF file. +Merges two or more SignNow documents into one single PDF file. Steps: @@ -317,9 +317,9 @@ public static partial class DocumentExamples /// /// Response with: /// - /// to sign the document via web browser using signNow credentials. + /// to sign the document via web browser using SignNow credentials. /// - /// to sign the document via web browser without signNow credentials. + /// to sign the document via web browser without SignNow credentials. /// public static async Task CreateSigningLinkToTheDocument(string documentId, Token token) @@ -343,7 +343,7 @@ More examples: [Create signing link][create_sign_lnk example], [Check signing st Simply upload a document and send it for signature right away. No need for adding fields and configuring roles. Just add the signer's email address and customize the message in your email. The document will be available for signing via a button in the email. -Clicking the button opens a document in signNow editor. Signers can click anywhere on a document to add their signature. +Clicking the button opens a document in SignNow editor. Signers can click anywhere on a document to add their signature. Remember: if your document contains even one fillable field, you have to create a role-based invite to get it signed. @@ -353,7 +353,7 @@ public static partial class InviteExamples /// /// Create a freeform invite to the document for signature. /// - /// signNow document you’d like to have signed + /// SignNow document you’d like to have signed /// The email of the invitee. /// Access token /// @@ -389,9 +389,9 @@ Role-based invites allow you to build e-signature workflows. The document can be Upload a document or create one from a template. The document will be available for signing via a button in the email. You can customize email messages for every signer. -Clicking the button opens a document in signNow editor. Signers can sign only the fields designated for their role. +Clicking the button opens a document in SignNow editor. Signers can sign only the fields designated for their role. -You can add more roles either in signNow web app while editing the fields, or with `ISignInvite` interface from SDK while specifying parameters of the `SignerOptions` object. +You can add more roles either in SignNow web app while editing the fields, or with `ISignInvite` interface from SDK while specifying parameters of the `SignerOptions` object. ```csharp public static partial class InviteExamples @@ -399,7 +399,7 @@ public static partial class InviteExamples /// /// Create a role-based invite to the document for signature. /// - /// signNow document with fields you’d like to have signed + /// SignNow document with fields you’d like to have signed /// The email of the invitee. /// Access token /// without any Identity of invite request. @@ -443,7 +443,7 @@ public static partial class InviteExamples /// /// Create an embedded signing invite to the document for signature. /// - /// signNow document you’d like to have signed + /// SignNow document you’d like to have signed /// The email of the invitee. /// Access token /// @@ -529,7 +529,7 @@ More examples: [Get document history][document_history example] ### Create Template by flattening the existing Document -Set required templateName and documentId parameters to create the signNow Template. +Set required templateName and documentId parameters to create the SignNow Template. ```csharp public static class DocumentExamples @@ -602,6 +602,80 @@ public async Task CreateDocumentGroupAsync() ``` More examples: [Document group operations][doc_group_operations example] +### Send a document group invite + +Sends a signing invite to a document group, then checks its status. + +```csharp +public async Task SendDocumentGroupInviteAsync(string documentGroupId, string documentId, string roleId, string roleName) +{ + var signNowContext = new SignNowContext(token); + + var request = new CreateGroupInviteRequest + { + InviteSteps = new List + { + new GroupInviteStep + { + Order = 1, + InviteEmails = new List + { + new GroupInviteEmail { Email = "signer@example.com", Role = roleName, RoleId = roleId, Order = 1 } + }, + InviteActions = new List + { + new GroupInviteAction { Email = "signer@example.com", RoleName = roleName, DocumentId = documentId } + } + } + } + }; + + var invite = await signNowContext.GroupInvites + .CreateGroupInviteAsync(documentGroupId, request) + .ConfigureAwait(false); + + // Check invite status, list pending signers, cancel/resend/reassign as needed + return await signNowContext.GroupInvites + .GetGroupInviteAsync(documentGroupId, invite.Data.Id) + .ConfigureAwait(false); +} +``` +More examples: [Document group invite operations][doc_group_invite_operations example] + +### Create an embedded session for a document group + +Creates an embedded signing invite for a document group and generates links for the embedded editor, embedded sending and embedded signing flows, without sending emails. + +```csharp +public async Task CreateDocumentGroupEmbeddedEditorLinkAsync(string documentGroupId) +{ + var signNowContext = new SignNowContext(token); + + var editorLink = await signNowContext.DocumentGroup + .GenerateDocumentGroupEmbeddedEditorLinkAsync(documentGroupId, new EmbeddedEditorOptions { LinkExpiration = 30 }) + .ConfigureAwait(false); + + return editorLink.Link; +} +``` +More examples: [Document group embedded operations][doc_group_embedded_operations example] + +### Get and update document group recipients + +Gets and updates the recipients, expiration, reminder and signing order settings for a document group. + +```csharp +public async Task GetDocumentGroupRecipientsAsync(string documentGroupId) +{ + var signNowContext = new SignNowContext(token); + + return await signNowContext.DocumentGroup + .GetDocumentGroupRecipientsAsync(documentGroupId) + .ConfigureAwait(false); +} +``` +More examples: [Document group recipients operations][doc_group_recipients_operations example] + ## Folders @@ -647,9 +721,9 @@ This SDK is distributed under the MIT License, see [LICENSE][license link] for m #### API Contact Information -If you have questions about the signNow API, please visit [signNow API Reference][api reference link] or email api@signnow.com. +If you have questions about the SignNow API, please visit [SignNow API Reference][api reference link] or email api@signnow.com. -**Support**: To contact signNow support, please email support@signnow.com or api@signnow.com. +**Support**: To contact SignNow support, please email support@signnow.com or api@signnow.com. **Sales**: For pricing information, please call (800) 831-2050, email sales@signnow.com or visit . @@ -662,7 +736,7 @@ If you have questions about the signNow API, please visit [signNow API Reference [nuget link]: https://www.nuget.org/packages/SignNow.Net [nuget downloads badge]: https://img.shields.io/nuget/dt/SignNow.Net.svg?style=flat-square [nuget downloads link]: https://www.nuget.org/packages/SignNow.Net "NuGet Downloads" -[license badge]: https://img.shields.io/github/license/signnow/SignNow.NET?style=flat-square "signNow .Net SDK License" +[license badge]: https://img.shields.io/github/license/signnow/SignNow.NET?style=flat-square "SignNow .Net SDK License" [license link]: https://github.com/signnow/SignNow.NET/blob/develop/LICENSE [api docs link]: https://docs.signnow.com [api reference link]: https://docs.signnow.com/docs/signnow/reference @@ -708,7 +782,10 @@ If you have questions about the signNow API, please visit [signNow API Reference [create_document example]: https://github.com/signnow/SignNow.NET/blob/develop/SignNow.Net.Examples/Documents/CreateDocumentFromTheTemplate.cs -[doc_group_operations example]: https://github.com/signnow/SignNow.NET/blob/develop/SignNow.Net.Examples/Document%20group/DocumentGroupOperations.cs +[doc_group_operations example]: https://github.com/signnow/SignNow.NET/blob/develop/SignNow.Net.Examples/Document%20group/DocumentGroupOperations.cs +[doc_group_invite_operations example]: https://github.com/signnow/SignNow.NET/blob/develop/SignNow.Net.Examples/Document%20group/DocumentGroupInviteOperations.cs +[doc_group_embedded_operations example]: https://github.com/signnow/SignNow.NET/blob/develop/SignNow.Net.Examples/Document%20group/DocumentGroupEmbeddedOperations.cs +[doc_group_recipients_operations example]: https://github.com/signnow/SignNow.NET/blob/develop/SignNow.Net.Examples/Document%20group/DocumentGroupRecipientsOperations.cs [get_all_folders example]: https://github.com/signnow/SignNow.NET/blob/develop/SignNow.Net.Examples/Folders/GetAllFolders.cs diff --git a/SignNow.Net.Examples/Document group/DocumentGroupEmbeddedOperations.cs b/SignNow.Net.Examples/Document group/DocumentGroupEmbeddedOperations.cs new file mode 100644 index 00000000..b9701bf4 --- /dev/null +++ b/SignNow.Net.Examples/Document group/DocumentGroupEmbeddedOperations.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests; +using SignNow.Net.Model.Requests.DocumentGroup; + +namespace SignNow.Net.Examples +{ + public partial class DocumentGroupOperations + { + [TestMethod] + public async Task CreateAndManageDocumentGroupEmbeddedInviteAsync() + { + // Upload two documents with signature fields so each has a role to invite + await using var fileStream = File.OpenRead(PdfWithSignatureField); + + var documents = new List(); + for (int i = 0; i < 2; i++) + { + var upload = await testContext.Documents + .UploadDocumentWithFieldExtractAsync(fileStream, $"ForDocumentGroupEmbeddedFile-{i}.pdf") + .ConfigureAwait(false); + var doc = await testContext.Documents.GetDocumentAsync(upload.Id).ConfigureAwait(false); + documents.Add(doc); + } + + // Create document group from uploaded documents + var documentGroup = await testContext.DocumentGroup + .CreateDocumentGroupAsync("DocumentGroupEmbeddedInviteTest", documents) + .ConfigureAwait(false); + + // Create an embedded signing invite for the document group, without sending emails + var embeddedInviteRequest = new CreateDocumentGroupEmbeddedInviteRequest + { + Invites = new List + { + new DocumentGroupEmbeddedInviteSigner + { + Email = "embedded-signer1@signnow.com", + RoleId = documents[0].Roles[0].Id, + SigningOrder = 1 + }, + new DocumentGroupEmbeddedInviteSigner + { + Email = "embedded-signer2@signnow.com", + RoleId = documents[1].Roles[0].Id, + SigningOrder = 1 + } + } + }; + + var embeddedInvite = await testContext.DocumentGroup + .CreateDocumentGroupEmbeddedInviteAsync(documentGroup.Id, embeddedInviteRequest) + .ConfigureAwait(false); + + Assert.AreEqual(2, embeddedInvite.InviteData.Count); + Console.WriteLine("Created {0} embedded invites for the document group", embeddedInvite.InviteData.Count); + + // Generate a signing link for the first embedded invite + var embeddedInviteLink = await testContext.DocumentGroup + .GenerateDocumentGroupEmbeddedInviteLinkAsync( + documentGroup.Id, + embeddedInvite.InviteData[0].Id, + new CreateDocumentGroupEmbedLinkOptions { LinkExpiration = 30 }) + .ConfigureAwait(false); + + Console.WriteLine("Embedded invite link: {0}", embeddedInviteLink.Link.AbsoluteUri); + + // Generate a link to open the embedded editor for the document group + var editorLink = await testContext.DocumentGroup + .GenerateDocumentGroupEmbeddedEditorLinkAsync( + documentGroup.Id, + new EmbeddedEditorOptions { LinkExpiration = 30 }) + .ConfigureAwait(false); + + Console.WriteLine("Embedded editor link: {0}", editorLink.Link.AbsoluteUri); + + // Generate a link to open the embedded sending workflow for the document group + var sendingLink = await testContext.DocumentGroup + .GenerateDocumentGroupEmbeddedSendingLinkAsync( + documentGroup.Id, + new EmbeddedSendingOptions { LinkExpiration = 30 }) + .ConfigureAwait(false); + + Console.WriteLine("Embedded sending link: {0}", sendingLink.Link.AbsoluteUri); + + // Cancel all embedded signing invites for the document group + await testContext.DocumentGroup + .CancelDocumentGroupEmbeddedInviteAsync(documentGroup.Id) + .ConfigureAwait(false); + + // Clean up + await testContext.DocumentGroup.DeleteDocumentGroupAsync(documentGroup.Id).ConfigureAwait(false); + foreach (var document in documents) + { + DeleteTestDocument(document.Id); + } + } + } +} diff --git a/SignNow.Net.Examples/Document group/DocumentGroupInviteOperations.cs b/SignNow.Net.Examples/Document group/DocumentGroupInviteOperations.cs new file mode 100644 index 00000000..37f991c5 --- /dev/null +++ b/SignNow.Net.Examples/Document group/DocumentGroupInviteOperations.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests.DocumentGroup; + +namespace SignNow.Net.Examples +{ + public partial class DocumentGroupOperations + { + [TestMethod] + public async Task SendAndManageDocumentGroupInviteAsync() + { + // Upload two documents with signature fields so each has a role to invite + await using var fileStream = File.OpenRead(PdfWithSignatureField); + + var documents = new List(); + for (int i = 0; i < 2; i++) + { + var upload = await testContext.Documents + .UploadDocumentWithFieldExtractAsync(fileStream, $"ForDocumentGroupInviteFile-{i}.pdf") + .ConfigureAwait(false); + var doc = await testContext.Documents.GetDocumentAsync(upload.Id).ConfigureAwait(false); + documents.Add(doc); + } + + // Create document group from uploaded documents + var documentGroup = await testContext.DocumentGroup + .CreateDocumentGroupAsync("SendDocumentGroupInviteTest", documents) + .ConfigureAwait(false); + + // Send a signing invite to the document group, one signer per document + var request = new CreateGroupInviteRequest + { + InviteSteps = new List + { + new GroupInviteStep + { + Order = 1, + InviteEmails = new List + { + new GroupInviteEmail + { + Email = "signer1@signnow.com", + Role = documents[0].Roles[0].Name, + RoleId = documents[0].Roles[0].Id, + Order = 1 + }, + new GroupInviteEmail + { + Email = "signer2@signnow.com", + Role = documents[1].Roles[0].Name, + RoleId = documents[1].Roles[0].Id, + Order = 1 + } + }, + InviteActions = new List + { + new GroupInviteAction + { + Email = "signer1@signnow.com", + RoleName = documents[0].Roles[0].Name, + DocumentId = documents[0].Id + }, + new GroupInviteAction + { + Email = "signer2@signnow.com", + RoleName = documents[1].Roles[0].Name, + DocumentId = documents[1].Id + } + } + } + } + }; + + var invite = await testContext.GroupInvites + .CreateGroupInviteAsync(documentGroup.Id, request) + .ConfigureAwait(false); + + Assert.AreEqual("pending", invite.Data.Status); + Console.WriteLine("Group invite created: {0} with status {1}", invite.Data.Id, invite.Data.Status); + + // Check the status of the group invite + var inviteStatus = await testContext.GroupInvites + .GetGroupInviteAsync(documentGroup.Id, invite.Data.Id) + .ConfigureAwait(false); + + Assert.AreEqual(invite.Data.Id, inviteStatus.Data.Id); + + // List signers who have not yet signed + var pendingInvites = await testContext.GroupInvites + .GetPendingGroupInvitesAsync(documentGroup.Id, invite.Data.Id) + .ConfigureAwait(false); + + Console.WriteLine("Pending signers: {0}", pendingInvites.Data.Count); + + // Reassign the first invite step to a different signer + var stepId = inviteStatus.Data.Steps.First().Id; + await testContext.GroupInvites + .ReassignSignerAsync(documentGroup.Id, invite.Data.Id, stepId, new ReassignSignerRequest + { + NewSigner = new ReassignSignerInfo + { + Email = "reassigned-signer@signnow.com", + RoleName = documents[0].Roles[0].Name + } + }) + .ConfigureAwait(false); + + Console.WriteLine("Reassigned invite step {0} to a new signer", stepId); + + // Resend invite emails to the current signers + await testContext.GroupInvites + .ResendGroupInviteAsync(documentGroup.Id, invite.Data.Id) + .ConfigureAwait(false); + + // Cancel the group invite + await testContext.GroupInvites + .CancelGroupInviteAsync(documentGroup.Id, invite.Data.Id) + .ConfigureAwait(false); + + // Clean up + await testContext.DocumentGroup.DeleteDocumentGroupAsync(documentGroup.Id).ConfigureAwait(false); + foreach (var document in documents) + { + DeleteTestDocument(document.Id); + } + } + } +} diff --git a/SignNow.Net.Examples/Document group/DocumentGroupRecipientsOperations.cs b/SignNow.Net.Examples/Document group/DocumentGroupRecipientsOperations.cs new file mode 100644 index 00000000..910b01f1 --- /dev/null +++ b/SignNow.Net.Examples/Document group/DocumentGroupRecipientsOperations.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests.DocumentGroup; +using SignNow.Net.Model.Responses; + +namespace SignNow.Net.Examples +{ + public partial class DocumentGroupOperations + { + [TestMethod] + public async Task GetAndUpdateDocumentGroupRecipientsAsync() + { + // Upload two documents with signature fields so each has a role to assign recipients to + await using var fileStream = File.OpenRead(PdfWithSignatureField); + + var documents = new List(); + for (int i = 0; i < 2; i++) + { + var upload = await testContext.Documents + .UploadDocumentWithFieldExtractAsync(fileStream, $"ForDocumentGroupRecipientsFile-{i}.pdf") + .ConfigureAwait(false); + var doc = await testContext.Documents.GetDocumentAsync(upload.Id).ConfigureAwait(false); + documents.Add(doc); + } + + // Create document group from uploaded documents + var documentGroup = await testContext.DocumentGroup + .CreateDocumentGroupAsync("DocumentGroupRecipientsTest", documents) + .ConfigureAwait(false); + + // Get current recipients, expiration, reminder and signing order settings + var recipients = await testContext.DocumentGroup + .GetDocumentGroupRecipientsAsync(documentGroup.Id) + .ConfigureAwait(false); + + Console.WriteLine("Document group has {0} recipient(s) configured", recipients.Data.Recipients?.Count ?? 0); + + // Assign a recipient to each document and configure expiration/reminder/order + var updateRequest = new UpdateDocumentGroupRecipientsRequest + { + Recipients = new List + { + new UpdateDocumentGroupRecipientEntry + { + Name = "Signer 1", + Email = "recipient1@signnow.com", + Order = 1, + Documents = new List + { + new DocumentGroupRecipientDocument + { + Id = documents[0].Id, + Role = documents[0].Roles[0].Name, + Action = "sign" + } + } + }, + new UpdateDocumentGroupRecipientEntry + { + Name = "Signer 2", + Email = "recipient2@signnow.com", + Order = 2, + Documents = new List + { + new DocumentGroupRecipientDocument + { + Id = documents[1].Id, + Role = documents[1].Roles[0].Name, + Action = "sign" + } + } + } + }, + GeneralExpirationDays = 30, + OrderType = "recipient_order" + }; + + await testContext.DocumentGroup + .UpdateDocumentGroupRecipientsAsync(documentGroup.Id, updateRequest) + .ConfigureAwait(false); + + Console.WriteLine("Document group recipients updated successfully"); + + // Clean up + await testContext.DocumentGroup.DeleteDocumentGroupAsync(documentGroup.Id).ConfigureAwait(false); + foreach (var document in documents) + { + DeleteTestDocument(document.Id); + } + } + } +} diff --git a/SignNow.Net.Test/UnitTests/Service/DocumentGroupEmbeddedTest.cs b/SignNow.Net.Test/UnitTests/Service/DocumentGroupEmbeddedTest.cs new file mode 100644 index 00000000..8bd3153a --- /dev/null +++ b/SignNow.Net.Test/UnitTests/Service/DocumentGroupEmbeddedTest.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests; +using SignNow.Net.Model.Requests.DocumentGroup; +using SignNow.Net.Model.Responses; +using SignNow.Net.Service; +using UnitTests; + +namespace UnitTests +{ + [TestClass] + public class DocumentGroupEmbeddedTest : SignNowTestBase + { + private const string GroupId = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + private const string EmbeddedInviteId = "b1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + + private static string MakeLinkJson(string url = "https://app.signnow.com/group/abc") + => JsonConvert.SerializeObject(new { data = new { link = url } }); + + [TestMethod] + public async Task CreateDocumentGroupEmbeddedInviteAsync_Success() + { + var expectedJson = JsonConvert.SerializeObject(new + { + data = new[] + { + new { id = "inviteId123", email = "s@example.com", role_id = "r1", order = 1, status = "pending" } + } + }); + var mockClient = SignNowClientMock(expectedJson); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + var request = new CreateDocumentGroupEmbeddedInviteRequest + { + Invites = new List + { + new DocumentGroupEmbeddedInviteSigner + { + Email = "s@example.com", + RoleId = "r1", + SigningOrder = 1 + } + } + }; + + var result = await service.CreateDocumentGroupEmbeddedInviteAsync(GroupId, request, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.AreEqual(1, result.InviteData.Count); + } + + [TestMethod] + public async Task GenerateDocumentGroupEmbeddedInviteLinkAsync_Success() + { + var mockClient = SignNowClientMock(MakeLinkJson()); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + var options = new CreateDocumentGroupEmbedLinkOptions { LinkExpiration = 30 }; + + var result = await service.GenerateDocumentGroupEmbeddedInviteLinkAsync(GroupId, EmbeddedInviteId, options, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result.Link, typeof(Uri)); + } + + [TestMethod] + public async Task CancelDocumentGroupEmbeddedInviteAsync_Success() + { + var mockClient = SignNowClientMock("{}"); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + await service.CancelDocumentGroupEmbeddedInviteAsync(GroupId, CancellationToken.None); + } + + [TestMethod] + public async Task GenerateDocumentGroupEmbeddedEditorLinkAsync_Success() + { + var mockClient = SignNowClientMock(MakeLinkJson("https://app.signnow.com/group-editor/abc")); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + var result = await service.GenerateDocumentGroupEmbeddedEditorLinkAsync( + GroupId, + new EmbeddedEditorOptions { LinkExpiration = 30 }, + CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result.Link, typeof(Uri)); + } + + [TestMethod] + public async Task GenerateDocumentGroupEmbeddedSendingLinkAsync_Success() + { + var mockClient = SignNowClientMock(MakeLinkJson("https://app.signnow.com/group-sending/abc")); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + var result = await service.GenerateDocumentGroupEmbeddedSendingLinkAsync( + GroupId, + new EmbeddedSendingOptions { LinkExpiration = 30 }, + CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result.Link, typeof(Uri)); + } + } +} diff --git a/SignNow.Net.Test/UnitTests/Service/DocumentGroupInviteTest.cs b/SignNow.Net.Test/UnitTests/Service/DocumentGroupInviteTest.cs new file mode 100644 index 00000000..fdc87218 --- /dev/null +++ b/SignNow.Net.Test/UnitTests/Service/DocumentGroupInviteTest.cs @@ -0,0 +1,143 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests.DocumentGroup; +using SignNow.Net.Model.Responses; +using SignNow.Net.Service; +using UnitTests; + +namespace UnitTests +{ + [TestClass] + public class DocumentGroupInviteTest : SignNowTestBase + { + private const string GroupId = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + private const string InviteId = "b1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + private const string StepId = "c1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + + private static string MakeInviteResponseJson() + => JsonConvert.SerializeObject(new + { + data = new + { + id = InviteId, + status = "pending", + created = 1700000000, + updated = 1700000000, + steps = new[] { new { id = StepId, order = 1, status = "pending" } } + } + }); + + [TestMethod] + public async Task CreateGroupInviteAsync_Success() + { + var mockClient = SignNowClientMock(MakeInviteResponseJson()); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + var request = new CreateGroupInviteRequest + { + InviteSteps = new List + { + new GroupInviteStep + { + Order = 1, + InviteEmails = new List + { + new GroupInviteEmail + { + Email = "signer@example.com", + Role = "Signer 1", + RoleId = "roleId123", + Order = 1 + } + }, + InviteActions = new List + { + new GroupInviteAction + { + Email = "signer@example.com", + RoleName = "Signer 1", + DocumentId = "docId123" + } + } + } + } + }; + + var result = await service.CreateGroupInviteAsync(GroupId, request, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsNotNull(result.Data); + Assert.AreEqual("pending", result.Data.Status); + } + + [TestMethod] + public async Task GetGroupInviteAsync_Success() + { + var mockClient = SignNowClientMock(MakeInviteResponseJson()); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + var result = await service.GetGroupInviteAsync(GroupId, InviteId, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.AreEqual(InviteId, result.Data.Id); + } + + [TestMethod] + public async Task CancelGroupInviteAsync_Success() + { + var mockClient = SignNowClientMock("{}"); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + await service.CancelGroupInviteAsync(GroupId, InviteId, CancellationToken.None); + } + + [TestMethod] + public async Task ResendGroupInviteAsync_Success() + { + var mockClient = SignNowClientMock("{}"); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + await service.ResendGroupInviteAsync(GroupId, InviteId, CancellationToken.None); + } + + [TestMethod] + public async Task GetPendingGroupInvitesAsync_Success() + { + var pendingJson = JsonConvert.SerializeObject(new + { + data = new[] + { + new { id = "pending1", email = "signer@example.com", role = "Signer 1", status = "pending" } + } + }); + var mockClient = SignNowClientMock(pendingJson); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + var result = await service.GetPendingGroupInvitesAsync(GroupId, InviteId, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.AreEqual(1, result.Data.Count); + Assert.AreEqual("signer@example.com", result.Data[0].Email); + } + + [TestMethod] + public async Task ReassignSignerAsync_Success() + { + var mockClient = SignNowClientMock("{}"); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + var request = new ReassignSignerRequest + { + NewSigner = new ReassignSignerInfo + { + Email = "new.signer@example.com", + RoleName = "Signer 1" + } + }; + + await service.ReassignSignerAsync(GroupId, InviteId, StepId, request, CancellationToken.None); + } + } +} diff --git a/SignNow.Net.Test/UnitTests/Service/DocumentGroupRecipientsTest.cs b/SignNow.Net.Test/UnitTests/Service/DocumentGroupRecipientsTest.cs new file mode 100644 index 00000000..9560c60e --- /dev/null +++ b/SignNow.Net.Test/UnitTests/Service/DocumentGroupRecipientsTest.cs @@ -0,0 +1,100 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests.DocumentGroup; +using SignNow.Net.Model.Responses; +using SignNow.Net.Service; +using UnitTests; + +namespace UnitTests +{ + [TestClass] + public class DocumentGroupRecipientsTest : SignNowTestBase + { + private const string DocumentGroupId = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + + [TestMethod] + public async Task GetDocumentGroupRecipientsAsync_Success() + { + var expectedResponse = new DocumentGroupRecipientsResponse + { + Data = new DocumentGroupRecipientsData + { + Recipients = new[] + { + new DocumentGroupRecipient + { + Name = "Signer 1", + Email = "signer@example.com", + Order = 1, + Documents = new[] + { + new DocumentGroupRecipientDocument + { + Id = "docId123", + Role = "Signer", + Action = "sign" + } + } + } + }, + Cc = new[] { "cc@example.com" }, + GeneralExpirationDays = 30, + GeneralReminder = new DocumentGroupRecipientReminder + { + RemindBefore = 5, + RemindAfter = 1, + RemindRepeat = 3 + }, + OrderType = "recipient_order" + } + }; + + var mockClient = SignNowClientMock(JsonConvert.SerializeObject(expectedResponse)); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + var result = await service.GetDocumentGroupRecipientsAsync(DocumentGroupId, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsNotNull(result.Data); + Assert.AreEqual(1, result.Data.Recipients.Count); + Assert.AreEqual("Signer 1", result.Data.Recipients[0].Name); + Assert.AreEqual("recipient_order", result.Data.OrderType); + } + + [TestMethod] + public async Task UpdateDocumentGroupRecipientsAsync_Success() + { + var mockClient = SignNowClientMock("{}"); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + var request = new UpdateDocumentGroupRecipientsRequest + { + Recipients = new List + { + new UpdateDocumentGroupRecipientEntry + { + Name = "Signer 1", + Email = "new.signer@example.com", + Order = 1, + Documents = new List + { + new DocumentGroupRecipientDocument + { + Id = "docId123", + Role = "Signer", + Action = "sign" + } + } + } + }, + GeneralExpirationDays = 30, + OrderType = "recipient_order" + }; + + await service.UpdateDocumentGroupRecipientsAsync(DocumentGroupId, request, CancellationToken.None); + } + } +} diff --git a/SignNow.Net.Test/UnitTests/Service/DocumentGroupTemplateRecipientsTest.cs b/SignNow.Net.Test/UnitTests/Service/DocumentGroupTemplateRecipientsTest.cs new file mode 100644 index 00000000..15be9fa6 --- /dev/null +++ b/SignNow.Net.Test/UnitTests/Service/DocumentGroupTemplateRecipientsTest.cs @@ -0,0 +1,86 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests.DocumentGroup; +using SignNow.Net.Model.Responses; +using SignNow.Net.Service; +using UnitTests; + +namespace UnitTests +{ + [TestClass] + public class DocumentGroupTemplateRecipientsTest : SignNowTestBase + { + private const string TemplateGroupId = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + + [TestMethod] + public async Task GetDocumentGroupTemplateRecipientsAsync_Success() + { + var expectedResponse = new DocumentGroupTemplateRecipientsResponse + { + Data = new DocumentGroupTemplateRecipientsData + { + InviteSteps = new[] + { + new DocumentGroupTemplateRecipientStep + { + Order = 1, + Recipients = new[] + { + new DocumentGroupTemplateRecipientRole + { + Id = "roleId123", + Name = "Signer 1", + SigningOrder = 1, + Email = "signer@example.com" + } + } + } + } + } + }; + + var mockClient = SignNowClientMock(JsonConvert.SerializeObject(expectedResponse)); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + + var result = await service.GetDocumentGroupTemplateRecipientsAsync(TemplateGroupId, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsNotNull(result.Data); + Assert.AreEqual(1, result.Data.InviteSteps.Count); + Assert.AreEqual("Signer 1", result.Data.InviteSteps[0].Recipients[0].Name); + } + + [TestMethod] + public async Task UpdateDocumentGroupTemplateRecipientsAsync_Success() + { + var mockClient = SignNowClientMock("{}"); + var service = new DocumentGroupService(ApiBaseUrl, new Token(), mockClient); + var request = new UpdateDocumentGroupTemplateRecipientsRequest + { + InviteSteps = new List + { + new DocumentGroupTemplateRecipientStep + { + Order = 1, + Recipients = new[] + { + new DocumentGroupTemplateRecipientRole + { + Id = "roleId123", + Name = "Signer 1", + SigningOrder = 1, + Email = "new.signer@example.com" + } + } + } + } + }; + + await service.UpdateDocumentGroupTemplateRecipientsAsync(TemplateGroupId, request, CancellationToken.None); + } + } +} diff --git a/SignNow.Net.Test/UnitTests/Service/EmbeddedEditorSendingTest.cs b/SignNow.Net.Test/UnitTests/Service/EmbeddedEditorSendingTest.cs new file mode 100644 index 00000000..f8491916 --- /dev/null +++ b/SignNow.Net.Test/UnitTests/Service/EmbeddedEditorSendingTest.cs @@ -0,0 +1,58 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests; +using SignNow.Net.Service; +using UnitTests; + +namespace UnitTests +{ + [TestClass] + public class EmbeddedEditorSendingTest : SignNowTestBase + { + private const string DocumentId = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4"; + + private static string MakeLinkJson(string url = "https://app.signnow.com/editor/abc123") + => JsonConvert.SerializeObject(new { data = new { link = url } }); + + [TestMethod] + public async Task GenerateEmbeddedEditorLinkAsync_Success() + { + var mockClient = SignNowClientMock(MakeLinkJson()); + var service = new UserService(ApiBaseUrl, new Token(), mockClient); + var options = new EmbeddedEditorOptions { LinkExpiration = 30 }; + + var result = await service.GenerateEmbeddedEditorLinkAsync(DocumentId, options, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result.Link, typeof(Uri)); + } + + [TestMethod] + public async Task GenerateEmbeddedSendingLinkAsync_Success() + { + var mockClient = SignNowClientMock(MakeLinkJson("https://app.signnow.com/sending/abc123")); + var service = new UserService(ApiBaseUrl, new Token(), mockClient); + var options = new EmbeddedSendingOptions { LinkExpiration = 60 }; + + var result = await service.GenerateEmbeddedSendingLinkAsync(DocumentId, options, CancellationToken.None); + + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result.Link, typeof(Uri)); + } + + [TestMethod] + public async Task GenerateEmbeddedEditorLinkAsync_NoExpiration_Success() + { + var mockClient = SignNowClientMock(MakeLinkJson()); + var service = new UserService(ApiBaseUrl, new Token(), mockClient); + + var result = await service.GenerateEmbeddedEditorLinkAsync(DocumentId, new EmbeddedEditorOptions(), CancellationToken.None); + + Assert.IsNotNull(result); + } + } +} diff --git a/SignNow.Net.Test/UnitTests/Service/EventSubscriptionV2Test.cs b/SignNow.Net.Test/UnitTests/Service/EventSubscriptionV2Test.cs new file mode 100644 index 00000000..1f59f488 --- /dev/null +++ b/SignNow.Net.Test/UnitTests/Service/EventSubscriptionV2Test.cs @@ -0,0 +1,42 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SignNow.Net.Model; +using SignNow.Net.Model.Requests; +using SignNow.Net.Service; +using UnitTests; + +namespace UnitTests +{ + [TestClass] + public class EventSubscriptionV2Test : SignNowTestBase + { + [TestMethod] + public async Task CreateEventSubscriptionV2Async_Success() + { + var mockClient = SignNowClientMock("{}"); + var service = new EventSubscriptionService(ApiBaseUrl, new Token(), mockClient); + var request = new CreateEventSubscriptionV2( + EventType.DocumentComplete, + "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4", + new Uri("https://example.com/webhook")); + + await service.CreateEventSubscriptionV2Async(request, CancellationToken.None); + // No exception = success (void return) + } + + [TestMethod] + public async Task CreateEventSubscriptionV2Async_NullCallback_Throws() + { + await Assert.ThrowsExceptionAsync(() => + { + var _ = new CreateEventSubscriptionV2( + EventType.DocumentComplete, + "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4a1b2c3d4", + null); + return Task.CompletedTask; + }); + } + } +} diff --git a/SignNow.Net/Interfaces/IDocumentGroup.cs b/SignNow.Net/Interfaces/IDocumentGroup.cs index 1060c05b..d8aabde1 100644 --- a/SignNow.Net/Interfaces/IDocumentGroup.cs +++ b/SignNow.Net/Interfaces/IDocumentGroup.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using SignNow.Net.Model; +using SignNow.Net.Model.Requests; using SignNow.Net.Model.Requests.DocumentGroup; using SignNow.Net.Model.Responses; @@ -111,5 +112,75 @@ public interface IDocumentGroup /// Propagates notification that operations should be canceled. /// Task GetDocumentGroupTemplatesAsync(GetDocumentGroupTemplatesRequest request, CancellationToken cancellationToken = default); + + /// + /// Gets the list of recipients for a document group template. + /// + /// ID of the Document Group Template. + /// Propagates notification that operations should be canceled. + Task GetDocumentGroupTemplateRecipientsAsync(string templateGroupId, CancellationToken cancellationToken = default); + + /// + /// Updates recipients for a document group template. + /// + /// ID of the Document Group Template. + /// Request containing updated invite steps and recipients. + /// Propagates notification that operations should be canceled. + Task UpdateDocumentGroupTemplateRecipientsAsync(string templateGroupId, UpdateDocumentGroupTemplateRecipientsRequest request, CancellationToken cancellationToken = default); + + /// + /// Gets the recipients, expiration, reminder and signing order settings for a document group. + /// + /// ID of the Document Group. + /// Propagates notification that operations should be canceled. + Task GetDocumentGroupRecipientsAsync(string documentGroupId, CancellationToken cancellationToken = default); + + /// + /// Updates recipients, expiration, reminder and signing order settings for a document group. + /// + /// ID of the Document Group. + /// Request containing updated recipients and signing settings. + /// Propagates notification that operations should be canceled. + Task UpdateDocumentGroupRecipientsAsync(string documentGroupId, UpdateDocumentGroupRecipientsRequest request, CancellationToken cancellationToken = default); + + /// + /// Creates an embedded signing invite for a document group without sending emails. + /// + /// ID of the Document Group. + /// Request containing invite details for each signer. + /// Propagates notification that operations should be canceled. + Task CreateDocumentGroupEmbeddedInviteAsync(string documentGroupId, CreateDocumentGroupEmbeddedInviteRequest request, CancellationToken cancellationToken = default); + + /// + /// Generates a link for an embedded document group signing invite. + /// + /// ID of the Document Group. + /// ID of the embedded invite. + /// Link generation options (auth method, expiration). + /// Propagates notification that operations should be canceled. + Task GenerateDocumentGroupEmbeddedInviteLinkAsync(string documentGroupId, string embeddedInviteId, CreateDocumentGroupEmbedLinkOptions options, CancellationToken cancellationToken = default); + + /// + /// Cancels all embedded signing invites for a document group. + /// + /// ID of the Document Group. + /// Propagates notification that operations should be canceled. + Task CancelDocumentGroupEmbeddedInviteAsync(string documentGroupId, CancellationToken cancellationToken = default); + + /// + /// Generates a link to open the embedded editor for a document group. + /// + /// ID of the Document Group. + /// Options for the embedded editor link. + /// Propagates notification that operations should be canceled. + Task GenerateDocumentGroupEmbeddedEditorLinkAsync(string documentGroupId, EmbeddedEditorOptions options, CancellationToken cancellationToken = default); + + /// + /// Generates a link to open the embedded sending workflow for a document group. + /// + /// ID of the Document Group. + /// Options for the embedded sending link. + /// Propagates notification that operations should be canceled. + Task GenerateDocumentGroupEmbeddedSendingLinkAsync(string documentGroupId, EmbeddedSendingOptions options, CancellationToken cancellationToken = default); } } diff --git a/SignNow.Net/Interfaces/IDocumentGroupInvite.cs b/SignNow.Net/Interfaces/IDocumentGroupInvite.cs new file mode 100644 index 00000000..527cef9b --- /dev/null +++ b/SignNow.Net/Interfaces/IDocumentGroupInvite.cs @@ -0,0 +1,65 @@ +using System.Threading; +using System.Threading.Tasks; +using SignNow.Net.Model.Requests.DocumentGroup; +using SignNow.Net.Model.Responses; + +namespace SignNow.Net.Interfaces +{ + /// + /// Interface for operations with Document Group Invites in signNow. + /// Allows sending, retrieving, canceling, resending, and managing signers + /// for group signing workflows. + /// + public interface IDocumentGroupInvite + { + /// + /// Sends an invite to sign a document group. + /// + /// ID of the Document Group. + /// Invite steps with signers and actions. + /// Propagates notification that operations should be canceled. + Task CreateGroupInviteAsync(string documentGroupId, CreateGroupInviteRequest request, CancellationToken cancellationToken = default); + + /// + /// Gets the status and details of a document group invite. + /// + /// ID of the Document Group. + /// ID of the group invite. + /// Propagates notification that operations should be canceled. + Task GetGroupInviteAsync(string documentGroupId, string inviteId, CancellationToken cancellationToken = default); + + /// + /// Cancels a document group invite. + /// + /// ID of the Document Group. + /// ID of the group invite. + /// Propagates notification that operations should be canceled. + Task CancelGroupInviteAsync(string documentGroupId, string inviteId, CancellationToken cancellationToken = default); + + /// + /// Resends invite emails for a document group. + /// + /// ID of the Document Group. + /// ID of the group invite. + /// Propagates notification that operations should be canceled. + Task ResendGroupInviteAsync(string documentGroupId, string inviteId, CancellationToken cancellationToken = default); + + /// + /// Gets the list of pending invites for a document group. + /// + /// ID of the Document Group. + /// ID of the group invite. + /// Propagates notification that operations should be canceled. + Task GetPendingGroupInvitesAsync(string documentGroupId, string inviteId, CancellationToken cancellationToken = default); + + /// + /// Reassigns a signer in a specific invite step of a document group workflow. + /// + /// ID of the Document Group. + /// ID of the group invite. + /// ID of the invite step to update. + /// New signer details. + /// Propagates notification that operations should be canceled. + Task ReassignSignerAsync(string documentGroupId, string inviteId, string stepId, ReassignSignerRequest request, CancellationToken cancellationToken = default); + } +} diff --git a/SignNow.Net/Interfaces/IEventSubscriptionService.cs b/SignNow.Net/Interfaces/IEventSubscriptionService.cs index 4533f1c1..8af6c48d 100644 --- a/SignNow.Net/Interfaces/IEventSubscriptionService.cs +++ b/SignNow.Net/Interfaces/IEventSubscriptionService.cs @@ -21,6 +21,13 @@ public interface IEventSubscriptionService /// Task CreateEventSubscriptionAsync(CreateEventSubscription createEvent, CancellationToken cancellationToken = default); + /// + /// Creates a new V2 event subscription using the /v2/event-subscriptions endpoint. + /// + /// Event subscription details. + /// Propagates notification that operations should be canceled. + Task CreateEventSubscriptionV2Async(CreateEventSubscriptionV2 createEvent, CancellationToken cancellationToken = default); + /// /// Gets information about all subscriptions to Events made with a specific application. /// diff --git a/SignNow.Net/Interfaces/ISignInvite.cs b/SignNow.Net/Interfaces/ISignInvite.cs index 38ce9bdb..76f161df 100644 --- a/SignNow.Net/Interfaces/ISignInvite.cs +++ b/SignNow.Net/Interfaces/ISignInvite.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using SignNow.Net.Model; using SignNow.Net.Model.Requests; +using SignNow.Net.Model.Responses; namespace SignNow.Net.Interfaces { @@ -69,5 +70,21 @@ public interface ISignInvite /// Propagates notification that operations should be canceled. /// Task ResendEmailInviteAsync(string fieldInviteId, CancellationToken cancellationToken = default); + + /// + /// Generates a link to open the embedded document editor. + /// + /// Identity of the document. + /// Options for the embedded editor link. + /// Propagates notification that operations should be canceled. + Task GenerateEmbeddedEditorLinkAsync(string documentId, EmbeddedEditorOptions options, CancellationToken cancellationToken = default); + + /// + /// Generates a link to open the embedded document sending workflow. + /// + /// Identity of the document. + /// Options for the embedded sending link. + /// Propagates notification that operations should be canceled. + Task GenerateEmbeddedSendingLinkAsync(string documentId, EmbeddedSendingOptions options, CancellationToken cancellationToken = default); } } diff --git a/SignNow.Net/Model/Requests/CreateEventSubscriptionV2.cs b/SignNow.Net/Model/Requests/CreateEventSubscriptionV2.cs new file mode 100644 index 00000000..63bfa994 --- /dev/null +++ b/SignNow.Net/Model/Requests/CreateEventSubscriptionV2.cs @@ -0,0 +1,18 @@ +using System; +using SignNow.Net.Extensions; +using SignNow.Net.Internal.Helpers; +using SignNow.Net.Model.Requests.EventSubscriptionBase; + +namespace SignNow.Net.Model.Requests +{ + public sealed class CreateEventSubscriptionV2 : AbstractEventSubscription + { + public CreateEventSubscriptionV2(EventType eventType, string entityId, Uri callbackUrl) + { + Guard.ArgumentNotNull(callbackUrl, nameof(callbackUrl)); + Event = eventType; + EntityId = entityId.ValidateId(); + Attributes.CallbackUrl = callbackUrl; + } + } +} diff --git a/SignNow.Net/Model/Requests/DocumentGroup/CreateDocumentGroupEmbedLinkOptions.cs b/SignNow.Net/Model/Requests/DocumentGroup/CreateDocumentGroupEmbedLinkOptions.cs new file mode 100644 index 00000000..29b1a251 --- /dev/null +++ b/SignNow.Net/Model/Requests/DocumentGroup/CreateDocumentGroupEmbedLinkOptions.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace SignNow.Net.Model.Requests.DocumentGroup +{ + public class CreateDocumentGroupEmbedLinkOptions : JsonHttpContent + { + [JsonProperty("auth_method")] + [JsonConverter(typeof(StringEnumConverter))] + public EmbeddedAuthType AuthMethod { get; set; } = EmbeddedAuthType.None; + + [JsonProperty("link_expiration", NullValueHandling = NullValueHandling.Ignore)] + public uint? LinkExpiration { get; set; } + } +} diff --git a/SignNow.Net/Model/Requests/DocumentGroup/CreateDocumentGroupEmbeddedInviteRequest.cs b/SignNow.Net/Model/Requests/DocumentGroup/CreateDocumentGroupEmbeddedInviteRequest.cs new file mode 100644 index 00000000..5062503f --- /dev/null +++ b/SignNow.Net/Model/Requests/DocumentGroup/CreateDocumentGroupEmbeddedInviteRequest.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace SignNow.Net.Model.Requests.DocumentGroup +{ + public class DocumentGroupEmbeddedInviteSigner + { + [JsonProperty("email")] + public string Email { get; set; } + + [JsonProperty("role_id")] + public string RoleId { get; set; } + + [JsonProperty("order")] + public uint SigningOrder { get; set; } + + [JsonProperty("auth_method")] + [JsonConverter(typeof(StringEnumConverter))] + public EmbeddedAuthType AuthMethod { get; set; } = EmbeddedAuthType.None; + } + + public class CreateDocumentGroupEmbeddedInviteRequest : JsonHttpContent + { + [JsonProperty("invites")] + public IList Invites { get; set; } = new List(); + } +} diff --git a/SignNow.Net/Model/Requests/DocumentGroup/CreateGroupInviteRequest.cs b/SignNow.Net/Model/Requests/DocumentGroup/CreateGroupInviteRequest.cs new file mode 100644 index 00000000..5439b0f3 --- /dev/null +++ b/SignNow.Net/Model/Requests/DocumentGroup/CreateGroupInviteRequest.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace SignNow.Net.Model.Requests.DocumentGroup +{ + public class GroupInviteEmail + { + [JsonProperty("email")] + public string Email { get; set; } + + [JsonProperty("role")] + public string Role { get; set; } + + [JsonProperty("role_id")] + public string RoleId { get; set; } + + [JsonProperty("order")] + public int Order { get; set; } + + [JsonProperty("subject", NullValueHandling = NullValueHandling.Ignore)] + public string Subject { get; set; } + + [JsonProperty("message", NullValueHandling = NullValueHandling.Ignore)] + public string Message { get; set; } + + [JsonProperty("expiration_days", NullValueHandling = NullValueHandling.Ignore)] + public int? ExpirationDays { get; set; } + + [JsonProperty("reminder", NullValueHandling = NullValueHandling.Ignore)] + public int? Reminder { get; set; } + } + + public class GroupInviteAction + { + [JsonProperty("email")] + public string Email { get; set; } + + [JsonProperty("role_name")] + public string RoleName { get; set; } + + [JsonProperty("action")] + public string Action { get; set; } = "sign"; + + [JsonProperty("document_id")] + public string DocumentId { get; set; } + + [JsonProperty("allow_reassign")] + public string AllowReassign { get; set; } = "0"; + + [JsonProperty("decline_by_signature")] + public string DeclineBySignature { get; set; } = "0"; + } + + public class GroupInviteStep + { + [JsonProperty("order")] + public int Order { get; set; } + + [JsonProperty("invite_emails")] + public IList InviteEmails { get; set; } = new List(); + + [JsonProperty("invite_actions")] + public IList InviteActions { get; set; } = new List(); + } + + public class CreateGroupInviteRequest : JsonHttpContent + { + [JsonProperty("invite_steps")] + public IList InviteSteps { get; set; } = new List(); + + [JsonProperty("cc", NullValueHandling = NullValueHandling.Ignore)] + public IList Cc { get; set; } + } +} diff --git a/SignNow.Net/Model/Requests/DocumentGroup/ReassignSignerRequest.cs b/SignNow.Net/Model/Requests/DocumentGroup/ReassignSignerRequest.cs new file mode 100644 index 00000000..16b96dde --- /dev/null +++ b/SignNow.Net/Model/Requests/DocumentGroup/ReassignSignerRequest.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace SignNow.Net.Model.Requests.DocumentGroup +{ + public class ReassignSignerInfo + { + [JsonProperty("email")] + public string Email { get; set; } + + [JsonProperty("role_name")] + public string RoleName { get; set; } + } + + public class ReassignSignerRequest : JsonHttpContent + { + [JsonProperty("new_signer")] + public ReassignSignerInfo NewSigner { get; set; } + } +} diff --git a/SignNow.Net/Model/Requests/DocumentGroup/UpdateDocumentGroupRecipientsRequest.cs b/SignNow.Net/Model/Requests/DocumentGroup/UpdateDocumentGroupRecipientsRequest.cs new file mode 100644 index 00000000..ca71a6fa --- /dev/null +++ b/SignNow.Net/Model/Requests/DocumentGroup/UpdateDocumentGroupRecipientsRequest.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using SignNow.Net.Model.Responses; + +namespace SignNow.Net.Model.Requests.DocumentGroup +{ + public class UpdateDocumentGroupRecipientEntry + { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("email")] + public string Email { get; set; } + + [JsonProperty("order")] + public int Order { get; set; } + + [JsonProperty("documents")] + public IList Documents { get; set; } = new List(); + } + + public class UpdateDocumentGroupRecipientsRequest : JsonHttpContent + { + [JsonProperty("recipients")] + public IList Recipients { get; set; } = new List(); + + [JsonProperty("cc", NullValueHandling = NullValueHandling.Ignore)] + public IList Cc { get; set; } + + [JsonProperty("general_expiration_days", NullValueHandling = NullValueHandling.Ignore)] + public int? GeneralExpirationDays { get; set; } + + [JsonProperty("general_reminder", NullValueHandling = NullValueHandling.Ignore)] + public DocumentGroupRecipientReminder GeneralReminder { get; set; } + + [JsonProperty("order_type", NullValueHandling = NullValueHandling.Ignore)] + public string OrderType { get; set; } + } +} diff --git a/SignNow.Net/Model/Requests/DocumentGroup/UpdateDocumentGroupTemplateRecipientsRequest.cs b/SignNow.Net/Model/Requests/DocumentGroup/UpdateDocumentGroupTemplateRecipientsRequest.cs new file mode 100644 index 00000000..6ee12398 --- /dev/null +++ b/SignNow.Net/Model/Requests/DocumentGroup/UpdateDocumentGroupTemplateRecipientsRequest.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using SignNow.Net.Model.Responses; + +namespace SignNow.Net.Model.Requests.DocumentGroup +{ + public class UpdateDocumentGroupTemplateRecipientsRequest : JsonHttpContent + { + [JsonProperty("invite_steps")] + public IList InviteSteps { get; set; } = new List(); + } +} diff --git a/SignNow.Net/Model/Requests/EmbeddedEditorOptions.cs b/SignNow.Net/Model/Requests/EmbeddedEditorOptions.cs new file mode 100644 index 00000000..8b9a2ee7 --- /dev/null +++ b/SignNow.Net/Model/Requests/EmbeddedEditorOptions.cs @@ -0,0 +1,19 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace SignNow.Net.Model.Requests +{ + public class EmbeddedEditorOptions : JsonHttpContent + { + [JsonProperty("link_expiration", NullValueHandling = NullValueHandling.Ignore)] + public uint? LinkExpiration { get; set; } + + [JsonProperty("redirect_uri", NullValueHandling = NullValueHandling.Ignore)] + public Uri RedirectUri { get; set; } + + [JsonProperty("redirect_target", NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(StringEnumConverter))] + public RedirectTarget? RedirectTarget { get; set; } + } +} diff --git a/SignNow.Net/Model/Requests/EmbeddedSendingOptions.cs b/SignNow.Net/Model/Requests/EmbeddedSendingOptions.cs new file mode 100644 index 00000000..d970d5d9 --- /dev/null +++ b/SignNow.Net/Model/Requests/EmbeddedSendingOptions.cs @@ -0,0 +1,19 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace SignNow.Net.Model.Requests +{ + public class EmbeddedSendingOptions : JsonHttpContent + { + [JsonProperty("link_expiration", NullValueHandling = NullValueHandling.Ignore)] + public uint? LinkExpiration { get; set; } + + [JsonProperty("redirect_uri", NullValueHandling = NullValueHandling.Ignore)] + public Uri RedirectUri { get; set; } + + [JsonProperty("redirect_target", NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(StringEnumConverter))] + public RedirectTarget? RedirectTarget { get; set; } + } +} diff --git a/SignNow.Net/Model/Responses/DocumentGroupEmbeddedInviteResponse.cs b/SignNow.Net/Model/Responses/DocumentGroupEmbeddedInviteResponse.cs new file mode 100644 index 00000000..af3ff1b0 --- /dev/null +++ b/SignNow.Net/Model/Responses/DocumentGroupEmbeddedInviteResponse.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using SignNow.Net.Model.Responses.GenericResponses; + +namespace SignNow.Net.Model.Responses +{ + public class DocumentGroupEmbeddedInviteData : IdResponse + { + [JsonProperty("email")] + public string Email { get; set; } + + [JsonProperty("role_id")] + public string RoleId { get; set; } + + [JsonProperty("order")] + public int Order { get; set; } + + [JsonProperty("status")] + [JsonConverter(typeof(StringEnumConverter))] + public InviteStatus Status { get; set; } + } + + public class DocumentGroupEmbeddedInviteResponse + { + [JsonProperty("data")] + public IReadOnlyList InviteData { get; internal set; } + } +} diff --git a/SignNow.Net/Model/Responses/DocumentGroupRecipientsResponse.cs b/SignNow.Net/Model/Responses/DocumentGroupRecipientsResponse.cs new file mode 100644 index 00000000..945935a9 --- /dev/null +++ b/SignNow.Net/Model/Responses/DocumentGroupRecipientsResponse.cs @@ -0,0 +1,140 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace SignNow.Net.Model.Responses +{ + public class DocumentGroupRecipientEmailGroup + { + [JsonProperty("id")] + public string Id { get; set; } + } + + public class DocumentGroupRecipientReminder + { + [JsonProperty("remind_before")] + public int RemindBefore { get; set; } + + [JsonProperty("remind_after")] + public int RemindAfter { get; set; } + + [JsonProperty("remind_repeat")] + public int RemindRepeat { get; set; } + } + + public class DocumentGroupRecipientAuthentication + { + [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] + public string Type { get; set; } + + [JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)] + public string Value { get; set; } + + [JsonProperty("phone", NullValueHandling = NullValueHandling.Ignore)] + public string Phone { get; set; } + + [JsonProperty("method", NullValueHandling = NullValueHandling.Ignore)] + public string Method { get; set; } + } + + public class DocumentGroupRecipientAttributes + { + [JsonProperty("message", NullValueHandling = NullValueHandling.Ignore)] + public string Message { get; set; } + + [JsonProperty("subject", NullValueHandling = NullValueHandling.Ignore)] + public string Subject { get; set; } + + [JsonProperty("expiration_days", NullValueHandling = NullValueHandling.Ignore)] + public int? ExpirationDays { get; set; } + + [JsonProperty("reminder", NullValueHandling = NullValueHandling.Ignore)] + public DocumentGroupRecipientReminder Reminder { get; set; } + + [JsonProperty("allow_forwarding", NullValueHandling = NullValueHandling.Ignore)] + public bool? AllowForwarding { get; set; } + + [JsonProperty("show_decline_button", NullValueHandling = NullValueHandling.Ignore)] + public bool? ShowDeclineButton { get; set; } + + [JsonProperty("i_am_recipient", NullValueHandling = NullValueHandling.Ignore)] + public bool? IAmRecipient { get; set; } + + [JsonProperty("authentication", NullValueHandling = NullValueHandling.Ignore)] + public DocumentGroupRecipientAuthentication Authentication { get; set; } + } + + public class DocumentGroupRecipientDocument + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("role")] + public string Role { get; set; } + + [JsonProperty("action")] + public string Action { get; set; } + } + + public class DocumentGroupRecipient + { + [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] + public string Name { get; set; } + + [JsonProperty("email", NullValueHandling = NullValueHandling.Ignore)] + public string Email { get; set; } + + [JsonProperty("email_group", NullValueHandling = NullValueHandling.Ignore)] + public DocumentGroupRecipientEmailGroup EmailGroup { get; set; } + + [JsonProperty("order")] + public int Order { get; set; } + + [JsonProperty("attributes", NullValueHandling = NullValueHandling.Ignore)] + public DocumentGroupRecipientAttributes Attributes { get; set; } + + [JsonProperty("documents")] + public IReadOnlyList Documents { get; set; } + } + + public class DocumentGroupUnmappedSignDocument + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("role")] + public string Role { get; set; } + + [JsonProperty("recipient", NullValueHandling = NullValueHandling.Ignore)] + public string Recipient { get; set; } + } + + public class DocumentGroupRecipientsData + { + [JsonProperty("recipients")] + public IReadOnlyList Recipients { get; set; } + + [JsonProperty("unmapped_documents", NullValueHandling = NullValueHandling.Ignore)] + public IReadOnlyList UnmappedDocuments { get; set; } + + [JsonProperty("allowed_unmapped_sign_documents", NullValueHandling = NullValueHandling.Ignore)] + public IReadOnlyList AllowedUnmappedSignDocuments { get; set; } + + [JsonProperty("cc", NullValueHandling = NullValueHandling.Ignore)] + public IReadOnlyList Cc { get; set; } + + [JsonProperty("general_expiration_days", NullValueHandling = NullValueHandling.Ignore)] + public int? GeneralExpirationDays { get; set; } + + [JsonProperty("general_reminder", NullValueHandling = NullValueHandling.Ignore)] + public DocumentGroupRecipientReminder GeneralReminder { get; set; } + + [JsonProperty("order_type", NullValueHandling = NullValueHandling.Ignore)] + public string OrderType { get; set; } + } + + public class DocumentGroupRecipientsResponse + { + [JsonProperty("data")] + public DocumentGroupRecipientsData Data { get; set; } + } +} diff --git a/SignNow.Net/Model/Responses/DocumentGroupTemplateRecipientsResponse.cs b/SignNow.Net/Model/Responses/DocumentGroupTemplateRecipientsResponse.cs new file mode 100644 index 00000000..56157e46 --- /dev/null +++ b/SignNow.Net/Model/Responses/DocumentGroupTemplateRecipientsResponse.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace SignNow.Net.Model.Responses +{ + public class DocumentGroupTemplateRecipientRole + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("signing_order")] + public int SigningOrder { get; set; } + + [JsonProperty("email", NullValueHandling = NullValueHandling.Ignore)] + public string Email { get; set; } + + [JsonProperty("first_name", NullValueHandling = NullValueHandling.Ignore)] + public string FirstName { get; set; } + + [JsonProperty("last_name", NullValueHandling = NullValueHandling.Ignore)] + public string LastName { get; set; } + } + + public class DocumentGroupTemplateRecipientStep + { + [JsonProperty("order")] + public int Order { get; set; } + + [JsonProperty("recipients")] + public IReadOnlyList Recipients { get; set; } + } + + public class DocumentGroupTemplateRecipientsData + { + [JsonProperty("invite_steps")] + public IReadOnlyList InviteSteps { get; set; } + } + + public class DocumentGroupTemplateRecipientsResponse + { + [JsonProperty("data")] + public DocumentGroupTemplateRecipientsData Data { get; set; } + } +} diff --git a/SignNow.Net/Model/Responses/GroupInviteResponse.cs b/SignNow.Net/Model/Responses/GroupInviteResponse.cs new file mode 100644 index 00000000..82647c0c --- /dev/null +++ b/SignNow.Net/Model/Responses/GroupInviteResponse.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using SignNow.Net.Internal.Helpers.Converters; +using SignNow.Net.Model.Responses.GenericResponses; + +namespace SignNow.Net.Model.Responses +{ + public class GroupInviteStepData + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("order")] + public int Order { get; set; } + + [JsonProperty("status")] + public string Status { get; set; } + } + + public class GroupInviteData : IdResponse + { + [JsonProperty("status")] + public string Status { get; set; } + + [JsonProperty("created")] + [JsonConverter(typeof(UnixTimeStampJsonConverter))] + public DateTime Created { get; set; } + + [JsonProperty("updated")] + [JsonConverter(typeof(UnixTimeStampJsonConverter))] + public DateTime Updated { get; set; } + + [JsonProperty("steps")] + public IReadOnlyList Steps { get; set; } + } + + public class GroupInviteResponse + { + [JsonProperty("data")] + public GroupInviteData Data { get; set; } + } +} diff --git a/SignNow.Net/Model/Responses/PendingGroupInvitesResponse.cs b/SignNow.Net/Model/Responses/PendingGroupInvitesResponse.cs new file mode 100644 index 00000000..6f870dd0 --- /dev/null +++ b/SignNow.Net/Model/Responses/PendingGroupInvitesResponse.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace SignNow.Net.Model.Responses +{ + public class PendingGroupInvite + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("email")] + public string Email { get; set; } + + [JsonProperty("role")] + public string Role { get; set; } + + [JsonProperty("status")] + public string Status { get; set; } + } + + public class PendingGroupInvitesResponse + { + [JsonProperty("data")] + public IReadOnlyList Data { get; set; } + } +} diff --git a/SignNow.Net/Service/DocumentGroupService.cs b/SignNow.Net/Service/DocumentGroupService.cs index 0358593b..d6b6f804 100644 --- a/SignNow.Net/Service/DocumentGroupService.cs +++ b/SignNow.Net/Service/DocumentGroupService.cs @@ -16,7 +16,7 @@ namespace SignNow.Net.Service { - public class DocumentGroupService : WebClientBase, IDocumentGroup + public class DocumentGroupService : WebClientBase, IDocumentGroup, IDocumentGroupInvite { /// /// Creates new instance of @@ -245,5 +245,250 @@ public async Task GetDocumentGroupTemplatesAs .RequestAsync(requestOptions, cancellationToken) .ConfigureAwait(false); } + + /// + public async Task GetDocumentGroupTemplateRecipientsAsync(string templateGroupId, CancellationToken cancellationToken = default) + { + Token.TokenType = TokenType.Bearer; + var requestOptions = new GetHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-group-templates/{templateGroupId.ValidateId()}/recipients"), + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task UpdateDocumentGroupTemplateRecipientsAsync(string templateGroupId, UpdateDocumentGroupTemplateRecipientsRequest request, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(request, nameof(request)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PutHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-group-templates/{templateGroupId.ValidateId()}/recipients"), + Content = request, + Token = Token + }; + + await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task GetDocumentGroupRecipientsAsync(string documentGroupId, CancellationToken cancellationToken = default) + { + Token.TokenType = TokenType.Bearer; + var requestOptions = new GetHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-groups/{documentGroupId.ValidateId()}/recipients"), + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task UpdateDocumentGroupRecipientsAsync(string documentGroupId, UpdateDocumentGroupRecipientsRequest request, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(request, nameof(request)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PutHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-groups/{documentGroupId.ValidateId()}/recipients"), + Content = request, + Token = Token + }; + + await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task CreateDocumentGroupEmbeddedInviteAsync(string documentGroupId, CreateDocumentGroupEmbeddedInviteRequest request, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(request, nameof(request)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-groups/{documentGroupId.ValidateId()}/embedded-invites"), + Content = request, + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task GenerateDocumentGroupEmbeddedInviteLinkAsync(string documentGroupId, string embeddedInviteId, CreateDocumentGroupEmbedLinkOptions options, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(options, nameof(options)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-groups/{documentGroupId.ValidateId()}/embedded-invites/{embeddedInviteId.ValidateId()}/link"), + Content = options, + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task CancelDocumentGroupEmbeddedInviteAsync(string documentGroupId, CancellationToken cancellationToken = default) + { + Token.TokenType = TokenType.Bearer; + var requestOptions = new DeleteHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-groups/{documentGroupId.ValidateId()}/embedded-invites"), + Token = Token + }; + + await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task GenerateDocumentGroupEmbeddedEditorLinkAsync(string documentGroupId, EmbeddedEditorOptions options, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(options, nameof(options)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-groups/{documentGroupId.ValidateId()}/embedded-editor"), + Content = options, + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task GenerateDocumentGroupEmbeddedSendingLinkAsync(string documentGroupId, EmbeddedSendingOptions options, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(options, nameof(options)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/document-groups/{documentGroupId.ValidateId()}/embedded-sending"), + Content = options, + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + // ===== IDocumentGroupInvite ===== + + /// + public async Task CreateGroupInviteAsync(string documentGroupId, CreateGroupInviteRequest request, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(request, nameof(request)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/documentgroup/{documentGroupId.ValidateId()}/groupinvite"), + Content = request, + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task GetGroupInviteAsync(string documentGroupId, string inviteId, CancellationToken cancellationToken = default) + { + Token.TokenType = TokenType.Bearer; + var requestOptions = new GetHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/documentgroup/{documentGroupId.ValidateId()}/groupinvite/{inviteId.ValidateId()}"), + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task CancelGroupInviteAsync(string documentGroupId, string inviteId, CancellationToken cancellationToken = default) + { + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/documentgroup/{documentGroupId.ValidateId()}/groupinvite/{inviteId.ValidateId()}/cancelinvite"), + Content = new EmptyPayloadRequest(), + Token = Token + }; + + await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task ResendGroupInviteAsync(string documentGroupId, string inviteId, CancellationToken cancellationToken = default) + { + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/documentgroup/{documentGroupId.ValidateId()}/groupinvite/{inviteId.ValidateId()}/resendinvites"), + Content = new EmptyPayloadRequest(), + Token = Token + }; + + await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task GetPendingGroupInvitesAsync(string documentGroupId, string inviteId, CancellationToken cancellationToken = default) + { + Token.TokenType = TokenType.Bearer; + var requestOptions = new GetHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/documentgroup/{documentGroupId.ValidateId()}/groupinvite/{inviteId.ValidateId()}/pendinginvites"), + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task ReassignSignerAsync(string documentGroupId, string inviteId, string stepId, ReassignSignerRequest request, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(request, nameof(request)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/documentgroup/{documentGroupId.ValidateId()}/groupinvite/{inviteId.ValidateId()}/invitestep/{stepId.ValidateId()}/update"), + Content = request, + Token = Token + }; + + await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } } } diff --git a/SignNow.Net/Service/EventSubscriptionService.cs b/SignNow.Net/Service/EventSubscriptionService.cs index 7668ff2b..2545240f 100644 --- a/SignNow.Net/Service/EventSubscriptionService.cs +++ b/SignNow.Net/Service/EventSubscriptionService.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using SignNow.Net.Interfaces; using SignNow.Net.Extensions; +using SignNow.Net.Internal.Helpers; using SignNow.Net.Model; using SignNow.Net.Model.Requests; using SignNow.Net.Model.Responses; @@ -38,6 +39,23 @@ await SignNowClient .ConfigureAwait(false); } + /// + public async Task CreateEventSubscriptionV2Async(CreateEventSubscriptionV2 createEvent, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(createEvent, nameof(createEvent)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, "/v2/event-subscriptions"), + Content = createEvent, + Token = Token + }; + + await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + /// public async Task GetEventSubscriptionsAsync(IQueryToString options, CancellationToken cancellationToken = default) { diff --git a/SignNow.Net/Service/UserService.cs b/SignNow.Net/Service/UserService.cs index 5520ccea..9382b375 100644 --- a/SignNow.Net/Service/UserService.cs +++ b/SignNow.Net/Service/UserService.cs @@ -294,6 +294,40 @@ public async Task ResendEmailInviteAsync(string fieldInviteId, CancellationToken await SignNowClient.RequestAsync(requestOptions, cancellationToken).ConfigureAwait(false); } + /// + public async Task GenerateEmbeddedEditorLinkAsync(string documentId, EmbeddedEditorOptions options, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(options, nameof(options)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/documents/{documentId.ValidateId()}/embedded-editor"), + Content = options, + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + + /// + public async Task GenerateEmbeddedSendingLinkAsync(string documentId, EmbeddedSendingOptions options, CancellationToken cancellationToken = default) + { + Guard.ArgumentNotNull(options, nameof(options)); + Token.TokenType = TokenType.Bearer; + var requestOptions = new PostHttpRequestOptions + { + RequestUrl = new Uri(ApiBaseUrl, $"/v2/documents/{documentId.ValidateId()}/embedded-sending"), + Content = options, + Token = Token + }; + + return await SignNowClient + .RequestAsync(requestOptions, cancellationToken) + .ConfigureAwait(false); + } + /// /// Returns an enumerable of user's documents. /// diff --git a/SignNow.Net/SignNowContext.cs b/SignNow.Net/SignNowContext.cs index 3e7df518..ea20aa50 100644 --- a/SignNow.Net/SignNowContext.cs +++ b/SignNow.Net/SignNowContext.cs @@ -33,6 +33,9 @@ public class SignNowContext : WebClientBase, ISignNowContext /// public IDocumentGroup DocumentGroup { get; protected set; } + /// + public IDocumentGroupInvite GroupInvites { get; protected set; } + /// /// Create all the services using single instance of and other dependencies. /// @@ -63,6 +66,7 @@ public SignNowContext(Uri baseApiUrl, Token token, ISignNowClient signNowClient Folders = new FolderService(ApiBaseUrl, Token, SignNowClient); Events = new EventSubscriptionService(ApiBaseUrl, Token, SignNowClient); DocumentGroup = new DocumentGroupService(ApiBaseUrl, Token, SignNowClient); + GroupInvites = (IDocumentGroupInvite) DocumentGroup; } ///