diff --git a/CHANGELOG.md b/CHANGELOG.md index da7cb609b..e9275f25f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # DocuSign C# Client Changelog +## [v8.0.0-rc2] - eSignature API v2.1-24.2.00.00 - 2024-07-23 +### Changed +- Introduced async versions of `ApiClient` authorization methods. +- Corrected SDK metadata. +- Updated the SDK release version. ## [v8.0.0-rc1] - eSignature API v2.1-24.2.00.00 - 2024-07-02 ### Breaking Changes diff --git a/LICENSE b/LICENSE index b1136e304..847de507f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2021 - DocuSign, Inc. (https://www.docusign.com) +Copyright (c) 2021 - Docusign, Inc. (https://www.docusign.com) 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 3558ff8fb..d92bd91cc 100755 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ This client SDK is provided as open source, which enables you to customize its f ### Version Information - **API version**: v2.1 -- **Latest SDK version (Including prerelease)**: 8.0.0-rc1 +- **Latest SDK version (Including prerelease)**: 8.0.0-rc2 ### Requirements diff --git a/sdk/.doxygenconfig b/sdk/.doxygenconfig index 9fc90763f..9eb3c8a6e 100644 --- a/sdk/.doxygenconfig +++ b/sdk/.doxygenconfig @@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "DocuSign CSharp Docs" +PROJECT_NAME = "Docusign CSharp Docs" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version diff --git a/sdk/DocuSign.eSign.sln b/sdk/DocuSign.eSign.sln index f8853d4d3..2145b3734 100644 --- a/sdk/DocuSign.eSign.sln +++ b/sdk/DocuSign.eSign.sln @@ -2,7 +2,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2012 VisualStudioVersion = 12.0.0.0 MinimumVisualStudioVersion = 10.0.0.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocuSign.eSign", "src\DocuSign.eSign\DocuSign.eSign.csproj", "{6832F73B-2498-44E8-A8A5-5DD858B47089}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocuSign.eSign", "src\DocuSign.eSign\DocuSign.eSign.csproj", "{0DD2EBA2-97AB-491E-911A-01F064CA09E7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -10,10 +10,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6832F73B-2498-44E8-A8A5-5DD858B47089}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6832F73B-2498-44E8-A8A5-5DD858B47089}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6832F73B-2498-44E8-A8A5-5DD858B47089}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6832F73B-2498-44E8-A8A5-5DD858B47089}.Release|Any CPU.Build.0 = Release|Any CPU + {0DD2EBA2-97AB-491E-911A-01F064CA09E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0DD2EBA2-97AB-491E-911A-01F064CA09E7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0DD2EBA2-97AB-491E-911A-01F064CA09E7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0DD2EBA2-97AB-491E-911A-01F064CA09E7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/sdk/src/DocuSign.eSign/Client/Auth/OAuth.cs b/sdk/src/DocuSign.eSign/Client/Auth/OAuth.cs index eb5af0c44..39a2cd396 100644 --- a/sdk/src/DocuSign.eSign/Client/Auth/OAuth.cs +++ b/sdk/src/DocuSign.eSign/Client/Auth/OAuth.cs @@ -54,7 +54,7 @@ public class OAuth * * UserInfo model with the following properties: *
sub: the user ID GUID. - *
accounts: this is list of DocuSign accounts associated with the current user. + *
accounts: this is list of Docusign accounts associated with the current user. *
name: the user's full name. *
givenName: the user's given name. *
familyName: the user's family name. @@ -73,7 +73,7 @@ public class UserInfo : IEquatable, IValidatableObject /// isDefault: whether this is the default account, when the user has access to multiple accounts. /// accountName: the human-readable name of the account. /// baseUri: the base URI associated with this account. - /// It also tells which DocuSign data center the account is hosted on. + /// It also tells which Docusign data center the account is hosted on. /// [DataContract] public class Account : IEquatable, IValidatableObject @@ -235,8 +235,8 @@ public IEnumerable Validate(ValidationContext validationContex /// /// Organization model with the following properties: - /// organizationId: the organization ID GUID if DocuSign Org Admin is enabled. - /// links: this is list of organization direct links associated with the DocuSign account. + /// organizationId: the organization ID GUID if Docusign Org Admin is enabled. + /// links: this is list of organization direct links associated with the Docusign account. /// [DataContract] public class Organization : IEquatable, IValidatableObject diff --git a/sdk/src/DocuSign.eSign/Client/Configuration.cs b/sdk/src/DocuSign.eSign/Client/Configuration.cs index fec7d14d2..072c5b0ad 100644 --- a/sdk/src/DocuSign.eSign/Client/Configuration.cs +++ b/sdk/src/DocuSign.eSign/Client/Configuration.cs @@ -26,7 +26,7 @@ public class Configuration /// Version of the package. /// /// Version of the package. - public const string Version = "8.0.0-rc1"; + public const string Version = "8.0.0-rc2"; /// /// Identifier for ISO 8601 DateTime Format diff --git a/sdk/src/DocuSign.eSign/Client/DocuSignClient.cs b/sdk/src/DocuSign.eSign/Client/DocuSignClient.cs index 73936138e..a3d373bbd 100644 --- a/sdk/src/DocuSign.eSign/Client/DocuSignClient.cs +++ b/sdk/src/DocuSign.eSign/Client/DocuSignClient.cs @@ -34,7 +34,7 @@ namespace DocuSign.eSign.Client { /// - /// DocuSignClient is mainly responsible for facilitating HTTP calls to the DocuSign APIs. + /// DocuSignClient is mainly responsible for facilitating HTTP calls to the Docusign APIs. /// public class DocuSignClient { @@ -739,17 +739,58 @@ public void SetOAuthBasePath(string oauthBaseUri = null) } } + private static T TryCatchWrapper(Func func) + { + try + { + return func.Invoke(); + } + catch (AggregateException ae) + { + foreach (var e in ae.InnerExceptions) + { + throw e; + } + + // This line is technically unreachable, but necessary for the compiler to be happy. + throw; + } + catch (Exception e) + { + + throw e; + } + } + /// /// GenerateAccessToken will exchange the authorization code for an access token and refresh tokens. /// /// OAuth2 client ID: Identifies the client making the request. - /// the secret key you generated when you set up the integration in DocuSign Admin console. + /// the secret key you generated when you set up the integration in Docusign Admin console. /// The authorization code that you received from the GetAuthorizationUri callback. /// OAuth.OAuthToken object. /// ApiException if the HTTP call status is different than 2xx. /// IOException if there is a problem while parsing the reponse object. /// public OAuth.OAuthToken GenerateAccessToken(string clientId, string clientSecret, string code) + { + CancellationTokenSource cts = new CancellationTokenSource(); + return TryCatchWrapper(() => GenerateAccessTokenAsync(clientId, clientSecret, code, cts.Token).ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + /// GenerateAccessTokenAsync will exchange the authorization code for an access token and refresh tokens. + /// + /// OAuth2 client ID: Identifies the client making the request. + /// the secret key you generated when you set up the integration in Docusign Admin console. + /// The authorization code that you received from the GetAuthorizationUri callback. + /// A CancellationToken which can be used to propagate notification that operations should be canceled. + /// + /// Task<OAuth.OAuthToken> object. + /// ApiException if the HTTP call status is different than 2xx. + /// IOException if there is a problem while parsing the reponse object. + /// + public async Task GenerateAccessTokenAsync(string clientId, string clientSecret, string code, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret) || string.IsNullOrEmpty(code)) { @@ -777,20 +818,13 @@ public OAuth.OAuthToken GenerateAccessToken(string clientId, string clientSecret foreach (var item in formParams) request.AddPostParameter(item.Key, item.Value); - DocuSignResponse response = RestClient.SendRequest(request); + DocuSignResponse response = await RestClient.SendRequestAsync(request, cancellationToken); if (response.StatusCode >= HttpStatusCode.OK && response.StatusCode < HttpStatusCode.BadRequest) { OAuth.OAuthToken tokenObj = JsonConvert.DeserializeObject(response.Content); // Add the token to this ApiClient string authHeader = "Bearer " + tokenObj.access_token; - if (!this.Configuration.DefaultHeader.ContainsKey("Authorization")) - { - this.Configuration.DefaultHeader.Add("Authorization", authHeader); - } - else - { - this.Configuration.DefaultHeader["Authorization"] = authHeader; - } + this.Configuration.DefaultHeader["Authorization"] = authHeader; return tokenObj; } else @@ -800,11 +834,23 @@ public OAuth.OAuthToken GenerateAccessToken(string clientId, string clientSecret } /// - /// Get User Info method takes the accessToken to retrieve User Account Data. + /// GetUserInfo method takes the accessToken to retrieve User Account Data. /// /// /// The User Info model. public OAuth.UserInfo GetUserInfo(string accessToken) + { + CancellationTokenSource cts = new CancellationTokenSource(); + return TryCatchWrapper(() => GetUserInfoAsync(accessToken, cts.Token).ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + /// GetUserInfoAsync method takes the accessToken to retrieve User Account Data. + /// + /// + /// A CancellationToken which can be used to propagate notification that operations should be canceled. + /// The User Info model. + public async Task GetUserInfoAsync(string accessToken, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(accessToken)) { @@ -817,7 +863,7 @@ public OAuth.UserInfo GetUserInfo(string accessToken) localVarHeaderParams.Add("Authorization", "Bearer " + accessToken); DocuSignRequest request = PrepareOAuthRequest(oAuthBasePath, $"oauth/userinfo", HttpMethod.Get, localVarHeaderParams.ToList(), localVarFormParams.ToList()); - DocuSignResponse response = RestClient.SendRequest(request); + DocuSignResponse response = await RestClient.SendRequestAsync(request, cancellationToken); if (response.StatusCode >= HttpStatusCode.OK && response.StatusCode < HttpStatusCode.BadRequest) { @@ -861,12 +907,12 @@ protected static RSA CreateRSAKeyFromPem(string key) } /// - /// Request JWT User Token - /// Configures the current instance of ApiClient with a fresh OAuth JWT access token from DocuSign + /// RequestJWTUserToken + /// Configures the current instance of ApiClient with a fresh OAuth JWT access token from Docusign /// - /// DocuSign OAuth Client Id(AKA Integrator Key) - /// DocuSign user Id to be impersonated(This is a UUID) - /// DocuSign OAuth base path + /// Docusign OAuth Client Id(AKA Integrator Key) + /// Docusign user Id to be impersonated(This is a UUID) + /// Docusign OAuth base path /// /// /// @@ -877,11 +923,34 @@ protected static RSA CreateRSAKeyFromPem(string key) /// /// The JWT user token public OAuth.OAuthToken RequestJWTUserToken(string clientId, string userId, string oauthBasePath, Stream privateKeyStream, int expiresInHours, List scopes = null) + { + CancellationTokenSource cts = new CancellationTokenSource(); + return TryCatchWrapper(() => RequestJWTUserTokenAsync(clientId, userId, oauthBasePath, privateKeyStream, expiresInHours, scopes, cts.Token).ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + /// RequestJWTUserTokenAsync + /// Configures the current instance of ApiClient with a fresh OAuth JWT access token from Docusign + /// + /// Docusign OAuth Client Id(AKA Integrator Key) + /// Docusign user Id to be impersonated(This is a UUID) + /// Docusign OAuth base path + /// + /// + /// + /// The Stream of the RSA private key + /// Number of hours remaining before the JWT assertion is considered as invalid + /// Optional. The list of requested scopes may include (but not limited to) + /// A CancellationToken which can be used to propagate notification that operations should be canceled. + /// + /// + /// The JWT user token + public Task RequestJWTUserTokenAsync(string clientId, string userId, string oauthBasePath, Stream privateKeyStream, int expiresInHours, List scopes = null, CancellationToken cancellationToken = default) { if (privateKeyStream != null && privateKeyStream.CanRead && privateKeyStream.Length > 0) { byte[] privateKeyBytes = ReadAsBytes(privateKeyStream); - return this.RequestJWTUserToken(clientId, userId, oauthBasePath, privateKeyBytes, expiresInHours, scopes); + return this.RequestJWTUserTokenAsync(clientId, userId, oauthBasePath, privateKeyBytes, expiresInHours, scopes, cancellationToken); } else { @@ -890,12 +959,12 @@ public OAuth.OAuthToken RequestJWTUserToken(string clientId, string userId, stri } /// - /// Request JWT User Token - /// Configures the current instance of ApiClient with a fresh OAuth JWT access token from DocuSign + /// RequestJWTUserToken + /// Configures the current instance of ApiClient with a fresh OAuth JWT access token from Docusign /// - /// DocuSign OAuth Client Id(AKA Integrator Key) - /// DocuSign user Id to be impersonated(This is a UUID) - /// DocuSign OAuth base path + /// Docusign OAuth Client Id(AKA Integrator Key) + /// Docusign user Id to be impersonated(This is a UUID) + /// Docusign OAuth base path /// /// /// @@ -906,6 +975,29 @@ public OAuth.OAuthToken RequestJWTUserToken(string clientId, string userId, stri /// /// The JWT user token public OAuth.OAuthToken RequestJWTUserToken(string clientId, string userId, string oauthBasePath, byte[] privateKeyBytes, int expiresInHours, List scopes = null) + { + CancellationTokenSource cts = new CancellationTokenSource(); + return TryCatchWrapper(() => RequestJWTUserTokenAsync(clientId, userId, oauthBasePath, privateKeyBytes, expiresInHours, scopes, cts.Token).ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + /// RequestJWTUserTokenAsync + /// Configures the current instance of ApiClient with a fresh OAuth JWT access token from Docusign + /// + /// Docusign OAuth Client Id(AKA Integrator Key) + /// Docusign user Id to be impersonated(This is a UUID) + /// Docusign OAuth base path + /// + /// + /// + /// The byte contents of the RSA private key + /// Number of hours remaining before the JWT assertion is considered as invalid + /// Optional. The list of requested scopes may include (but not limited to) You can also pass any advanced scope. + /// A CancellationToken which can be used to propagate notification that operations should be canceled. + /// + /// + /// The JWT user token + public async Task RequestJWTUserTokenAsync(string clientId, string userId, string oauthBasePath, byte[] privateKeyBytes, int expiresInHours, List scopes = null, CancellationToken cancellationToken = default) { string privateKey = Encoding.UTF8.GetString(privateKeyBytes); @@ -956,19 +1048,13 @@ public OAuth.OAuthToken RequestJWTUserToken(string clientId, string userId, stri }; DocuSignRequest request = PrepareOAuthRequest(oauthBasePath, $"oauth/token", HttpMethod.Post, Configuration.DefaultHeader?.ToList(), localVarFormParams.ToList()); - DocuSignResponse response = RestClient.SendRequest(request); + DocuSignResponse response = await RestClient.SendRequestAsync(request, cancellationToken); if (response.StatusCode >= HttpStatusCode.OK && response.StatusCode < HttpStatusCode.BadRequest) { OAuth.OAuthToken tokenInfo = JsonConvert.DeserializeObject(response.Content); - if (!this.Configuration.DefaultHeader.ContainsKey("Authorization")) - { - this.Configuration.DefaultHeader.Add("Authorization", string.Format("{0} {1}", tokenInfo.token_type, tokenInfo.access_token)); - } - else - { - this.Configuration.DefaultHeader["Authorization"] = string.Format("{0} {1}", tokenInfo.token_type, tokenInfo.access_token); - } + this.Configuration.DefaultHeader["Authorization"] = string.Format("{0} {1}", tokenInfo.token_type, tokenInfo.access_token); + return tokenInfo; } else @@ -978,10 +1064,10 @@ public OAuth.OAuthToken RequestJWTUserToken(string clientId, string userId, stri } /// - /// *RESERVED FOR PARTNERS* Request JWT Application Token + /// *RESERVED FOR PARTNERS* RequestJWTApplicationToken /// - /// DocuSign OAuth Client Id(AKA Integrator Key) - /// DocuSign OAuth base path + /// Docusign OAuth Client Id(AKA Integrator Key) + /// Docusign OAuth base path /// /// /// @@ -992,6 +1078,27 @@ public OAuth.OAuthToken RequestJWTUserToken(string clientId, string userId, stri /// /// The JWT application token public OAuth.OAuthToken RequestJWTApplicationToken(string clientId, string oauthBasePath, byte[] privateKeyBytes, int expiresInHours, List scopes = null) + { + CancellationTokenSource cts = new CancellationTokenSource(); + return TryCatchWrapper(() => RequestJWTApplicationTokenAsync(clientId, oAuthBasePath, privateKeyBytes, expiresInHours, scopes, cts.Token).ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + /// *RESERVED FOR PARTNERS* RequestJWTApplicationTokenAsync + /// + /// Docusign OAuth Client Id(AKA Integrator Key) + /// Docusign OAuth base path + /// + /// + /// + /// The byte contents of the RSA private key + /// Number of hours remaining before the JWT assertion is considered as invalid + /// Optional. The list of requested scopes may include (but not limited to) You can also pass any advanced scope. + /// A CancellationToken which can be used to propagate notification that operations should be canceled. + /// + /// + /// The JWT application token + public async Task RequestJWTApplicationTokenAsync(string clientId, string oauthBasePath, byte[] privateKeyBytes, int expiresInHours, List scopes = null, CancellationToken cancellationToken = default) { string privateKey = Encoding.UTF8.GetString(privateKeyBytes); @@ -1029,19 +1136,12 @@ public OAuth.OAuthToken RequestJWTApplicationToken(string clientId, string oauth }; DocuSignRequest request = PrepareOAuthRequest(oauthBasePath, $"oauth/token", HttpMethod.Post, Configuration.DefaultHeader?.ToList(), localVarFormParams.ToList()); - DocuSignResponse response = RestClient.SendRequest(request); + DocuSignResponse response = await RestClient.SendRequestAsync(request, cancellationToken); if (response.StatusCode >= HttpStatusCode.OK && response.StatusCode < HttpStatusCode.BadRequest) { OAuth.OAuthToken tokenInfo = JsonConvert.DeserializeObject(response.Content); - if (!this.Configuration.DefaultHeader.ContainsKey("Authorization")) - { - this.Configuration.DefaultHeader.Add("Authorization", string.Format("{0} {1}", tokenInfo.token_type, tokenInfo.access_token)); - } - else - { - this.Configuration.DefaultHeader["Authorization"] = string.Format("{0} {1}", tokenInfo.token_type, tokenInfo.access_token); - } + this.Configuration.DefaultHeader["Authorization"] = string.Format("{0} {1}", tokenInfo.token_type, tokenInfo.access_token); return tokenInfo; } else diff --git a/sdk/src/DocuSign.eSign/DocuSign.eSign.csproj b/sdk/src/DocuSign.eSign/DocuSign.eSign.csproj index c23d11cbe..d00929b19 100644 --- a/sdk/src/DocuSign.eSign/DocuSign.eSign.csproj +++ b/sdk/src/DocuSign.eSign/DocuSign.eSign.csproj @@ -7,7 +7,7 @@ The Docusign NuGet package makes integrating Docusign into your apps and websites a super fast and painless process. The library is open sourced on GitHub, look for the docusign-esign-csharp-client repository. Join the eSign revolution! DocuSign Inc. DocuSign - Copyright © DocuSign 2024 + Copyright © Docusign 2024 DocuSign.eSign DocuSign Library @@ -16,17 +16,17 @@ DocuSign.eSign DocuSign.eSign en-US - 8.0.0-rc1 + 8.0.0-rc2 true true - DocuSign.eSign;REST;eSign;docusign;eSignature;api - https://s.gravatar.com/avatar/4a8c033df6baa902f730d514d5574c33 - https://github.com/docusign/docusign-csharp-client - https://github.com/docusign/docusign-csharp-client/blob/master/LICENSE - https://github.com/docusign/docusign-csharp-client + Docusign.eSign;REST;eSign;docusign;eSignature;api + icon.png + https://github.com/docusign/docusign-esign-csharp-client + https://github.com/docusign/docusign-esign-csharp-client/blob/master/LICENSE + https://github.com/docusign/docusign-esign-csharp-client git - [v8.0.0-rc1] - ESignature API v2.1-24.2.00.00 - 7/2/2024 + [v8.0.0-rc2] - ESignature API v2.1-24.2.00.00 - 7/23/2024 NET462 diff --git a/sdk/src/DocuSign.eSign/Properties/AssemblyInfo.cs b/sdk/src/DocuSign.eSign/Properties/AssemblyInfo.cs index 28b6e24dc..00080a37b 100644 --- a/sdk/src/DocuSign.eSign/Properties/AssemblyInfo.cs +++ b/sdk/src/DocuSign.eSign/Properties/AssemblyInfo.cs @@ -22,5 +22,5 @@ // [assembly: AssemblyVersion("1.0.*")] internal class AssemblyInformation { - public const string AssemblyInformationalVersion = "8.0.0-rc1"; + public const string AssemblyInformationalVersion = "8.0.0-rc2"; } \ No newline at end of file diff --git a/sdk/src/DocuSign.eSign/icon.png b/sdk/src/DocuSign.eSign/icon.png new file mode 100644 index 000000000..739fd6acf Binary files /dev/null and b/sdk/src/DocuSign.eSign/icon.png differ