From 6c699c7e17b6232b4900d37aa0e73059450ac1c3 Mon Sep 17 00:00:00 2001 From: box-sdk-build Date: Thu, 29 Feb 2024 03:36:25 -0800 Subject: [PATCH 1/3] feat: add local to Function, Field and Type to mark as private for Python (box/box-codegen#440) --- .codegen.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 .codegen.json diff --git a/.codegen.json b/.codegen.json new file mode 100644 index 00000000..4f3c2ef7 --- /dev/null +++ b/.codegen.json @@ -0,0 +1 @@ +{ "engineHash": "ee76251", "specHash": "b2f7568", "version": "0.1.0" } From 5560f2334d50e568d20a5b83bd7e99510ff97ca5 Mon Sep 17 00:00:00 2001 From: box-sdk-build Date: Thu, 29 Feb 2024 08:48:49 -0800 Subject: [PATCH 2/3] feat: add method to authentication classes (box/box-codegen#442) Closes: SDK-3600 --- .codegen.json | 2 +- .../Test/Auth/AuthManagerTests.cs | 7 +++---- Box.Sdk.Gen/Box/CcgAuth/BoxCcgAuth.cs | 5 +++++ .../Box/DeveloperTokenAuth/BoxDeveloperTokenAuth.cs | 5 +++++ Box.Sdk.Gen/Box/JwtAuth/BoxJwtAuth.cs | 5 +++++ Box.Sdk.Gen/Box/Oauth/BoxOAuth.cs | 6 +++++- Box.Sdk.Gen/Networking/Auth/IAuthentication.cs | 4 +++- Box.Sdk.Gen/Networking/Fetch.cs | 4 ++-- 8 files changed, 29 insertions(+), 9 deletions(-) diff --git a/.codegen.json b/.codegen.json index 4f3c2ef7..359dd7af 100644 --- a/.codegen.json +++ b/.codegen.json @@ -1 +1 @@ -{ "engineHash": "ee76251", "specHash": "b2f7568", "version": "0.1.0" } +{ "engineHash": "905c6a0", "specHash": "b2f7568", "version": "0.1.0" } diff --git a/Box.Sdk.Gen.Tests.Integration/Test/Auth/AuthManagerTests.cs b/Box.Sdk.Gen.Tests.Integration/Test/Auth/AuthManagerTests.cs index 6a5bdf68..27eafc25 100644 --- a/Box.Sdk.Gen.Tests.Integration/Test/Auth/AuthManagerTests.cs +++ b/Box.Sdk.Gen.Tests.Integration/Test/Auth/AuthManagerTests.cs @@ -134,13 +134,12 @@ public async System.Threading.Tasks.Task TestDeveloperTokenAuth() { public async System.Threading.Tasks.Task TestOauthAuthRevoke() { OAuthConfig config = new OAuthConfig(clientId: Utils.GetEnvVar(name: "CLIENT_ID"), clientSecret: Utils.GetEnvVar(name: "CLIENT_SECRET")); BoxOAuth auth = new BoxOAuth(config: config); + BoxClient client = new BoxClient(auth: auth); AccessToken token = await GetAccessTokenAsync().ConfigureAwait(false); await auth.TokenStorage.StoreAsync(token: token).ConfigureAwait(false); - AccessToken? tokenBeforeRevoke = await auth.TokenStorage.GetAsync().ConfigureAwait(false); + await client.Users.GetUserMeAsync().ConfigureAwait(false); await auth.RevokeTokenAsync().ConfigureAwait(false); - AccessToken? tokenAfterRevoke = await auth.TokenStorage.GetAsync().ConfigureAwait(false); - Assert.IsTrue(tokenBeforeRevoke != null); - Assert.IsTrue(tokenAfterRevoke == null); + await Assert.That.IsExceptionAsync(async() => await client.Users.GetUserMeAsync().ConfigureAwait(false)); } [TestMethod] diff --git a/Box.Sdk.Gen/Box/CcgAuth/BoxCcgAuth.cs b/Box.Sdk.Gen/Box/CcgAuth/BoxCcgAuth.cs index 66ad7629..cde63ddf 100644 --- a/Box.Sdk.Gen/Box/CcgAuth/BoxCcgAuth.cs +++ b/Box.Sdk.Gen/Box/CcgAuth/BoxCcgAuth.cs @@ -63,6 +63,11 @@ public async System.Threading.Tasks.Task RetrieveTokenAsync(Network return oldToken; } + public async System.Threading.Tasks.Task RetrieveAuthorizationHeaderAsync(NetworkSession? networkSession = null) { + AccessToken token = await this.RetrieveTokenAsync(networkSession: networkSession).ConfigureAwait(false); + return string.Concat("Bearer ", token.AccessTokenField); + } + /// /// Create a new BoxCCGAuth instance that uses the provided user ID as the subject ID. /// May be one of this application's created App User. Depending on the configured User Access Level, may also be any other App User or Managed User in the enterprise. diff --git a/Box.Sdk.Gen/Box/DeveloperTokenAuth/BoxDeveloperTokenAuth.cs b/Box.Sdk.Gen/Box/DeveloperTokenAuth/BoxDeveloperTokenAuth.cs index 9941c48e..fa5d9104 100644 --- a/Box.Sdk.Gen/Box/DeveloperTokenAuth/BoxDeveloperTokenAuth.cs +++ b/Box.Sdk.Gen/Box/DeveloperTokenAuth/BoxDeveloperTokenAuth.cs @@ -30,5 +30,10 @@ public async System.Threading.Tasks.Task RefreshTokenAsync(NetworkS throw new BoxSdkError(message: "Developer token has expired. Please provide a new one."); } + public async System.Threading.Tasks.Task RetrieveAuthorizationHeaderAsync(NetworkSession? networkSession = null) { + AccessToken token = await this.RetrieveTokenAsync(networkSession: networkSession).ConfigureAwait(false); + return string.Concat("Bearer ", token.AccessTokenField); + } + } } \ No newline at end of file diff --git a/Box.Sdk.Gen/Box/JwtAuth/BoxJwtAuth.cs b/Box.Sdk.Gen/Box/JwtAuth/BoxJwtAuth.cs index 254de8bb..3cf734df 100644 --- a/Box.Sdk.Gen/Box/JwtAuth/BoxJwtAuth.cs +++ b/Box.Sdk.Gen/Box/JwtAuth/BoxJwtAuth.cs @@ -74,6 +74,11 @@ public async System.Threading.Tasks.Task RetrieveTokenAsync(Network return oldToken; } + public async System.Threading.Tasks.Task RetrieveAuthorizationHeaderAsync(NetworkSession? networkSession = null) { + AccessToken token = await this.RetrieveTokenAsync(networkSession: networkSession).ConfigureAwait(false); + return string.Concat("Bearer ", token.AccessTokenField); + } + /// /// Create a new BoxJWTAuth instance that uses the provided user ID as the subject of the JWT assertion. /// May be one of this application's created App User. Depending on the configured User Access Level, may also be any other App User or Managed User in the enterprise. diff --git a/Box.Sdk.Gen/Box/Oauth/BoxOAuth.cs b/Box.Sdk.Gen/Box/Oauth/BoxOAuth.cs index 1a42db2c..41b3c8b1 100644 --- a/Box.Sdk.Gen/Box/Oauth/BoxOAuth.cs +++ b/Box.Sdk.Gen/Box/Oauth/BoxOAuth.cs @@ -81,6 +81,11 @@ public async System.Threading.Tasks.Task RefreshTokenAsync(NetworkS return token; } + public async System.Threading.Tasks.Task RetrieveAuthorizationHeaderAsync(NetworkSession? networkSession = null) { + AccessToken token = await this.RetrieveTokenAsync(networkSession: networkSession).ConfigureAwait(false); + return string.Concat("Bearer ", token.AccessTokenField); + } + /// /// Revoke an active Access Token, effectively logging a user out that has been previously authenticated. /// @@ -94,7 +99,6 @@ public async System.Threading.Tasks.Task RevokeTokenAsync(NetworkSession? networ } AuthorizationManager authManager = networkSession != null ? new AuthorizationManager(networkSession: networkSession) : new AuthorizationManager(); await authManager.RevokeAccessTokenAsync(requestBody: new PostOAuth2Revoke() { ClientId = this.Config.ClientId, ClientSecret = this.Config.ClientSecret, Token = token.AccessTokenField }).ConfigureAwait(false); - await this.TokenStorage.ClearAsync().ConfigureAwait(false); } /// diff --git a/Box.Sdk.Gen/Networking/Auth/IAuthentication.cs b/Box.Sdk.Gen/Networking/Auth/IAuthentication.cs index 95c5e451..8d9b99ae 100644 --- a/Box.Sdk.Gen/Networking/Auth/IAuthentication.cs +++ b/Box.Sdk.Gen/Networking/Auth/IAuthentication.cs @@ -6,7 +6,9 @@ namespace Box.Sdk.Gen { public interface IAuthentication { public System.Threading.Tasks.Task RetrieveTokenAsync(NetworkSession? networkSession = null); - public System.Threading.Tasks.Task RefreshTokenAsync(NetworkSession networkSession); + public System.Threading.Tasks.Task RefreshTokenAsync(NetworkSession? networkSession = null); + + public System.Threading.Tasks.Task RetrieveAuthorizationHeaderAsync(NetworkSession? networkSession = null); } } \ No newline at end of file diff --git a/Box.Sdk.Gen/Networking/Fetch.cs b/Box.Sdk.Gen/Networking/Fetch.cs index 786ec671..f989d249 100644 --- a/Box.Sdk.Gen/Networking/Fetch.cs +++ b/Box.Sdk.Gen/Networking/Fetch.cs @@ -148,8 +148,8 @@ private static async Task BuildHttpRequest(string resource, if (options.Auth != null) { - var token = await options.Auth.RetrieveTokenAsync().ConfigureAwait(false); - httpRequest.Headers.Add("Authorization", $"Bearer {token.AccessTokenField}"); + var authHeaderValue = await options.Auth.RetrieveAuthorizationHeaderAsync(options.NetworkSession).ConfigureAwait(false); + httpRequest.Headers.Add("Authorization", authHeaderValue); } return httpRequest; From f86bce83c18f10e994a20a018c31b5e75491d3a6 Mon Sep 17 00:00:00 2001 From: box-sdk-build Date: Fri, 1 Mar 2024 00:48:24 -0800 Subject: [PATCH 3/3] feat: throw BoxApiError in C# fetch, add additional data to ResponseInfo (box/box-codegen#439) --- .codegen.json | 2 +- Box.Sdk.Gen/Box/ApiException.cs | 54 ------------------ Box.Sdk.Gen/Box/BoxSdkError.cs | 17 ------ Box.Sdk.Gen/Box/CcgAuth/BoxCcgAuth.cs | 2 +- .../BoxDeveloperTokenAuth.cs | 2 +- Box.Sdk.Gen/Box/Errors/BoxApiException.cs | 17 ++++++ Box.Sdk.Gen/Box/Errors/BoxSdkException.cs | 19 +++++++ Box.Sdk.Gen/Box/Errors/RequestInfo.cs | 26 +++++++++ Box.Sdk.Gen/Box/Errors/ResponseInfo.cs | 56 +++++++++++++++++++ Box.Sdk.Gen/Box/JwtAuth/BoxJwtAuth.cs | 4 +- Box.Sdk.Gen/Box/Oauth/BoxOAuth.cs | 4 +- Box.Sdk.Gen/Networking/Fetch.cs | 51 +++++++++++++---- 12 files changed, 166 insertions(+), 88 deletions(-) delete mode 100644 Box.Sdk.Gen/Box/ApiException.cs delete mode 100644 Box.Sdk.Gen/Box/BoxSdkError.cs create mode 100644 Box.Sdk.Gen/Box/Errors/BoxApiException.cs create mode 100644 Box.Sdk.Gen/Box/Errors/BoxSdkException.cs create mode 100644 Box.Sdk.Gen/Box/Errors/RequestInfo.cs create mode 100644 Box.Sdk.Gen/Box/Errors/ResponseInfo.cs diff --git a/.codegen.json b/.codegen.json index 359dd7af..ae0f0c9f 100644 --- a/.codegen.json +++ b/.codegen.json @@ -1 +1 @@ -{ "engineHash": "905c6a0", "specHash": "b2f7568", "version": "0.1.0" } +{ "engineHash": "df5b5de", "specHash": "b2f7568", "version": "0.1.0" } diff --git a/Box.Sdk.Gen/Box/ApiException.cs b/Box.Sdk.Gen/Box/ApiException.cs deleted file mode 100644 index 60792b24..00000000 --- a/Box.Sdk.Gen/Box/ApiException.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; - -namespace Fetch -{ - /// - /// Class representing exception caused most likely by the Http/s request. - /// - public class ApiException : Exception - { - /// - /// Status code of the Http/s response. - /// - public int StatusCode { get; } - - /// - /// Http/s response in a string format. - /// - public string ApiResponse { get; } - - /// - /// Instantiates a new ApiException with the default message - /// - /// Status code returned from the api - /// Response returned from the api - public ApiException(int statusCode, string apiResponse) - : base(BaseErrorMessage(statusCode, apiResponse)) - { - StatusCode = statusCode; - ApiResponse = apiResponse; - } - - /// - /// Instantiates a new ApiException with the provided message - /// - /// Status code returned from the api - /// Response returned from the api - /// Custom error message - public ApiException(int statusCode, string apiResponse, string message) - : base(message) - { - StatusCode = statusCode; - ApiResponse = apiResponse; - } - internal static string BuildApiExceptionMessage(int statusCode, string apiResponse, string? message = null) - { - return message != null ? - string.Join(Environment.NewLine, message, BaseErrorMessage(statusCode, apiResponse)) : - BaseErrorMessage(statusCode, apiResponse); - } - - private static string BaseErrorMessage(int statusCode, string apiResponse) - => $"Api returned an error status code. Status code: {statusCode}, API response: {apiResponse}"; - } -} diff --git a/Box.Sdk.Gen/Box/BoxSdkError.cs b/Box.Sdk.Gen/Box/BoxSdkError.cs deleted file mode 100644 index f0a6a737..00000000 --- a/Box.Sdk.Gen/Box/BoxSdkError.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace Errors -{ - public class BoxSdkError : Exception { - - public System.DateTimeOffset? Timestamp { get; set; } = default; - - public string? Error { get; set; } = default; - - public string Name { get; set; } - - public BoxSdkError(string message, string name = "BoxSdkError") : base(message) { - Name = name; - } - } -} \ No newline at end of file diff --git a/Box.Sdk.Gen/Box/CcgAuth/BoxCcgAuth.cs b/Box.Sdk.Gen/Box/CcgAuth/BoxCcgAuth.cs index cde63ddf..7e9d585b 100644 --- a/Box.Sdk.Gen/Box/CcgAuth/BoxCcgAuth.cs +++ b/Box.Sdk.Gen/Box/CcgAuth/BoxCcgAuth.cs @@ -119,7 +119,7 @@ public BoxCcgAuth AsEnterprise(string enterpriseId, ITokenStorage? tokenStorage public async System.Threading.Tasks.Task DownscopeTokenAsync(IReadOnlyList scopes, string? resource = null, string? sharedLink = null, NetworkSession? networkSession = null) { AccessToken? token = await this.TokenStorage.GetAsync().ConfigureAwait(false); if (token == null) { - throw new BoxSdkError(message: "No access token is available. Make an API call to retrieve a token before calling this method."); + throw new BoxSdkException(message: "No access token is available. Make an API call to retrieve a token before calling this method."); } AuthorizationManager authManager = networkSession != null ? new AuthorizationManager(networkSession: networkSession) : new AuthorizationManager(); AccessToken downscopedToken = await authManager.RequestAccessTokenAsync(requestBody: new PostOAuth2Token(grantType: PostOAuth2TokenGrantTypeField.UrnIetfParamsOauthGrantTypeTokenExchange) { SubjectToken = token.AccessTokenField, SubjectTokenType = PostOAuth2TokenSubjectTokenTypeField.UrnIetfParamsOauthTokenTypeAccessToken, Resource = resource, Scope = string.Join(" ", scopes), BoxSharedLink = sharedLink }).ConfigureAwait(false); diff --git a/Box.Sdk.Gen/Box/DeveloperTokenAuth/BoxDeveloperTokenAuth.cs b/Box.Sdk.Gen/Box/DeveloperTokenAuth/BoxDeveloperTokenAuth.cs index fa5d9104..c37b3b71 100644 --- a/Box.Sdk.Gen/Box/DeveloperTokenAuth/BoxDeveloperTokenAuth.cs +++ b/Box.Sdk.Gen/Box/DeveloperTokenAuth/BoxDeveloperTokenAuth.cs @@ -27,7 +27,7 @@ public async System.Threading.Tasks.Task RetrieveTokenAsync(Network /// An object to keep network session state /// public async System.Threading.Tasks.Task RefreshTokenAsync(NetworkSession? networkSession = null) { - throw new BoxSdkError(message: "Developer token has expired. Please provide a new one."); + throw new BoxSdkException(message: "Developer token has expired. Please provide a new one."); } public async System.Threading.Tasks.Task RetrieveAuthorizationHeaderAsync(NetworkSession? networkSession = null) { diff --git a/Box.Sdk.Gen/Box/Errors/BoxApiException.cs b/Box.Sdk.Gen/Box/Errors/BoxApiException.cs new file mode 100644 index 00000000..210e1dc0 --- /dev/null +++ b/Box.Sdk.Gen/Box/Errors/BoxApiException.cs @@ -0,0 +1,17 @@ +using System; + +namespace Errors +{ + public class BoxApiException : BoxSdkException + { + public RequestInfo RequestInfo { get; set; } + + public ResponseInfo ResponseInfo { get; set; } + + public BoxApiException(string message, DateTimeOffset timeStamp, RequestInfo requestInfo, ResponseInfo responseInfo) : base(message, timeStamp, "BoxApiException") + { + RequestInfo = requestInfo; + ResponseInfo = responseInfo; + } + } +} \ No newline at end of file diff --git a/Box.Sdk.Gen/Box/Errors/BoxSdkException.cs b/Box.Sdk.Gen/Box/Errors/BoxSdkException.cs new file mode 100644 index 00000000..8418ba20 --- /dev/null +++ b/Box.Sdk.Gen/Box/Errors/BoxSdkException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Errors +{ + public class BoxSdkException : Exception + { + public System.DateTimeOffset? Timestamp { get; set; } = default; + + public string? Error { get; set; } = default; + + public string Name { get; set; } + + public BoxSdkException(string message, DateTimeOffset? timeStamp = null, string name = "BoxSdkException") : base(message) + { + Name = name; + Timestamp = timeStamp ?? DateTimeOffset.UtcNow; + } + } +} \ No newline at end of file diff --git a/Box.Sdk.Gen/Box/Errors/RequestInfo.cs b/Box.Sdk.Gen/Box/Errors/RequestInfo.cs new file mode 100644 index 00000000..a9ae4e18 --- /dev/null +++ b/Box.Sdk.Gen/Box/Errors/RequestInfo.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace Errors +{ + public class RequestInfo + { + public string Method { get; set; } + + public string Url { get; set; } + + public IReadOnlyDictionary QueryParams { get; set; } + + public IReadOnlyDictionary Headers { get; set; } + + public string? Body { get; set; } = default; + + public RequestInfo(string method, string? url, IReadOnlyDictionary? queryParams, IReadOnlyDictionary headers) + { + Method = method; + Url = url ?? ""; + QueryParams = queryParams ?? new ReadOnlyDictionary(new Dictionary()); + Headers = headers; + } + } +} \ No newline at end of file diff --git a/Box.Sdk.Gen/Box/Errors/ResponseInfo.cs b/Box.Sdk.Gen/Box/Errors/ResponseInfo.cs new file mode 100644 index 00000000..5c25e39a --- /dev/null +++ b/Box.Sdk.Gen/Box/Errors/ResponseInfo.cs @@ -0,0 +1,56 @@ +using Serialization.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Errors +{ + public class ResponseInfo + { + public int StatusCode { get; set; } + + public IReadOnlyDictionary Headers { get; set; } + + public SerializedData? Body { get; set; } = default; + + public string? RawBody { get; set; } = default; + + public string? Code { get; set; } = default; + + public Dictionary? ContextInfo { get; set; } = default; + + public string? RequestId { get; set; } = default; + + public string? HelpUrl { get; set; } = default; + + public ResponseInfo(int statusCode, IReadOnlyDictionary headers, SerializedData body, string rawBody, + string? code, Dictionary? contextInfo, string? requestId, string? helpUrl) + { + StatusCode = statusCode; + Headers = headers; + Body = body; + RawBody = rawBody; + Code = code; + ContextInfo = contextInfo ?? new Dictionary(); + RequestId = requestId; + HelpUrl = helpUrl; + } + } + + internal class BoxApiExceptionDetails + { + [JsonPropertyName("code")] + public string? Code { get; set; } + + [JsonPropertyName("context_info")] + public Dictionary? ContextInfo { get; set; } + + [JsonPropertyName("request_id")] + public string? RequestId { get; set; } + + [JsonPropertyName("help_url")] + public string? HelpUrl { get; set; } + + public BoxApiExceptionDetails() { } + } + +} \ No newline at end of file diff --git a/Box.Sdk.Gen/Box/JwtAuth/BoxJwtAuth.cs b/Box.Sdk.Gen/Box/JwtAuth/BoxJwtAuth.cs index 3cf734df..ce8e15d3 100644 --- a/Box.Sdk.Gen/Box/JwtAuth/BoxJwtAuth.cs +++ b/Box.Sdk.Gen/Box/JwtAuth/BoxJwtAuth.cs @@ -46,7 +46,7 @@ public BoxJwtAuth(JwtConfig config) { /// public async System.Threading.Tasks.Task RefreshTokenAsync(NetworkSession? networkSession = null) { if (Utils.IsBrowser()) { - throw new BoxSdkError(message: "JWT auth is not supported in browser environment."); + throw new BoxSdkException(message: "JWT auth is not supported in browser environment."); } JwtAlgorithm alg = this.Config.Algorithm != null ? NullableUtils.Unwrap(this.Config.Algorithm) : JwtAlgorithm.Rs256; Dictionary claims = new Dictionary() { { "exp", Utils.GetEpochTimeInSeconds() + 30 }, { "box_sub_type", this.SubjectType } }; @@ -132,7 +132,7 @@ public BoxJwtAuth AsEnterprise(string userId, ITokenStorage? tokenStorage = defa public async System.Threading.Tasks.Task DownscopeTokenAsync(IReadOnlyList scopes, string? resource = null, string? sharedLink = null, NetworkSession? networkSession = null) { AccessToken? token = await this.TokenStorage.GetAsync().ConfigureAwait(false); if (token == null) { - throw new BoxSdkError(message: "No access token is available. Make an API call to retrieve a token before calling this method."); + throw new BoxSdkException(message: "No access token is available. Make an API call to retrieve a token before calling this method."); } AuthorizationManager authManager = networkSession != null ? new AuthorizationManager(networkSession: networkSession) : new AuthorizationManager(); AccessToken downscopedToken = await authManager.RequestAccessTokenAsync(requestBody: new PostOAuth2Token(grantType: PostOAuth2TokenGrantTypeField.UrnIetfParamsOauthGrantTypeTokenExchange) { SubjectToken = token.AccessTokenField, SubjectTokenType = PostOAuth2TokenSubjectTokenTypeField.UrnIetfParamsOauthTokenTypeAccessToken, Resource = resource, Scope = string.Join(" ", scopes), BoxSharedLink = sharedLink }).ConfigureAwait(false); diff --git a/Box.Sdk.Gen/Box/Oauth/BoxOAuth.cs b/Box.Sdk.Gen/Box/Oauth/BoxOAuth.cs index 41b3c8b1..08ca4531 100644 --- a/Box.Sdk.Gen/Box/Oauth/BoxOAuth.cs +++ b/Box.Sdk.Gen/Box/Oauth/BoxOAuth.cs @@ -61,7 +61,7 @@ public async System.Threading.Tasks.Task GetTokensAuthorizationCode public async System.Threading.Tasks.Task RetrieveTokenAsync(NetworkSession? networkSession = null) { AccessToken? token = await this.TokenStorage.GetAsync().ConfigureAwait(false); if (token == null) { - throw new BoxSdkError(message: "Access and refresh tokens not available. Authenticate before making any API call first."); + throw new BoxSdkException(message: "Access and refresh tokens not available. Authenticate before making any API call first."); } return token; } @@ -119,7 +119,7 @@ public async System.Threading.Tasks.Task RevokeTokenAsync(NetworkSession? networ public async System.Threading.Tasks.Task DownscopeTokenAsync(IReadOnlyList scopes, string? resource = null, string? sharedLink = null, NetworkSession? networkSession = null) { AccessToken? token = await this.TokenStorage.GetAsync().ConfigureAwait(false); if (token == null || token.AccessTokenField == null) { - throw new BoxSdkError(message: "No access token is available."); + throw new BoxSdkException(message: "No access token is available."); } AuthorizationManager authManager = networkSession != null ? new AuthorizationManager(networkSession: networkSession) : new AuthorizationManager(); AccessToken downscopedToken = await authManager.RequestAccessTokenAsync(requestBody: new PostOAuth2Token(grantType: PostOAuth2TokenGrantTypeField.UrnIetfParamsOauthGrantTypeTokenExchange) { SubjectToken = token.AccessTokenField, SubjectTokenType = PostOAuth2TokenSubjectTokenTypeField.UrnIetfParamsOauthTokenTypeAccessToken, Scope = string.Join(" ", scopes), Resource = resource, BoxSharedLink = sharedLink }).ConfigureAwait(false); diff --git a/Box.Sdk.Gen/Networking/Fetch.cs b/Box.Sdk.Gen/Networking/Fetch.cs index f989d249..024a2f60 100644 --- a/Box.Sdk.Gen/Networking/Fetch.cs +++ b/Box.Sdk.Gen/Networking/Fetch.cs @@ -1,4 +1,5 @@ using Box.Sdk.Gen; +using Errors; using Microsoft.Extensions.DependencyInjection; using Serializer; using System; @@ -31,7 +32,7 @@ static HttpClientAdapter() var httpClientFactory = serviceProvider.GetService(); if (httpClientFactory == null) { - throw new ArgumentException("Unable to create HttpClient. Cannot get an IHttpClientFactory instance from a ServiceProvider."); + throw new BoxSdkException("Unable to create HttpClient. Cannot get an IHttpClientFactory instance from a ServiceProvider."); } _clientFactory = httpClientFactory; } @@ -67,7 +68,7 @@ public static async Task FetchAsync(string resource, FetchOptions if (attempt >= networkSession.RetryAttempts) { - throw await BuildApiException(response, statusCode, cancellationToken, "Max retry attempts excedeed.").ConfigureAwait(false); + throw await BuildApiException(request, response, options, statusCode, cancellationToken, "Max retry attempts excedeed.").ConfigureAwait(false); } if (statusCode == 401) @@ -87,7 +88,7 @@ public static async Task FetchAsync(string resource, FetchOptions } else { - throw await BuildApiException(response, statusCode, cancellationToken).ConfigureAwait(false); + throw await BuildApiException(request, response, options, statusCode, cancellationToken).ConfigureAwait(false); } response?.Dispose(); @@ -96,13 +97,43 @@ public static async Task FetchAsync(string resource, FetchOptions } - private static async Task BuildApiException(HttpResponseMessage? response, int statusCode, System.Threading.CancellationToken cancellationToken, string? message = null) + private static async Task BuildApiException(HttpRequestMessage request, HttpResponseMessage? response, FetchOptions options, + int statusCode, System.Threading.CancellationToken cancellationToken, string? message = null) { - var responseContent = response != null ? await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false) : "empty"; + if (message != null) + { + return new BoxSdkException(message, DateTimeOffset.UtcNow); + } + + string responseContent; + Dictionary responseHeaders; + + if (response != null) + { + responseContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + responseHeaders = response.Headers.Where(x => x.Value.Any()).ToDictionary(x => x.Key, x => x.Value.First()); + } + else + { + responseContent = "empty"; + responseHeaders = new Dictionary(); + } + response?.Dispose(); - return message != null ? - new ApiException(statusCode, responseContent, ApiException.BuildApiExceptionMessage(statusCode, responseContent, message)) : - new ApiException(statusCode, responseContent); + + var requestHeaders = request.Headers + .Where(x => x.Value.Any()) + .ToDictionary(x => x.Key, x => x.Value.First()); + + var requestInfo = new RequestInfo(request.Method.ToString(), request.RequestUri?.ToString(), options.Parameters, requestHeaders); + + var responseAsSerializedData = JsonUtils.JsonToSerializedData(responseContent); + var errorDetails = SimpleJsonSerializer.Deserialize(responseAsSerializedData); + + var responseInfo = new ResponseInfo(statusCode, responseHeaders, responseAsSerializedData, responseContent, errorDetails.Code, errorDetails.ContextInfo, + errorDetails.RequestId, errorDetails.HelpUrl); + + return new BoxApiException(responseContent, DateTimeOffset.UtcNow, requestInfo, responseInfo); } private static async Task BuildHttpRequest(string resource, FetchOptions options) @@ -177,7 +208,7 @@ private static async Task ExecuteRequest(HttpClient client, if (options.MultipartData == null) { - throw new ArgumentException("Could not upload file. MultipartData on FetchOptions is null"); + throw new BoxSdkException("Could not upload file. MultipartData on FetchOptions is null"); } foreach (var part in options.MultipartData) @@ -186,7 +217,7 @@ private static async Task ExecuteRequest(HttpClient client, new StreamContent(part.FileStream) : part.Data != null ? new StringContent(JsonUtils.SdToJson(part.Data)) : - throw new ArgumentException($"HttpContent for MultipartData {part} not found"); + throw new BoxSdkException($"HttpContent for MultipartData {part} not found"); // for avatar upload if (part.ContentType != null && part.FileName != null)