Skip to content

Commit

Permalink
Merge branch 'release/0.52.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Jericho committed Oct 27, 2022
2 parents e287ae4 + e663442 commit c42a165
Show file tree
Hide file tree
Showing 22 changed files with 189 additions and 104 deletions.
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"cake.tool": {
"version": "2.2.0",
"version": "2.3.0",
"commands": [
"dotnet-cake"
]
Expand Down
1 change: 1 addition & 0 deletions Source/ZoomNet.IntegrationTests/TestsRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ public async Task<int> RunAsync()
// Get my user and permisisons
var myUser = await client.Users.GetCurrentAsync(source.Token).ConfigureAwait(false);
var myPermissions = await client.Users.GetCurrentPermissionsAsync(source.Token).ConfigureAwait(false);
Array.Sort(myPermissions); // Sort permissions alphabetically for convenience

// Execute the async tests in parallel (with max degree of parallelism)
var results = await integrationTests.ForEachAsync(
Expand Down
2 changes: 1 addition & 1 deletion Source/ZoomNet.UnitTests/Models/DashboardParticipant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public void Parse_json()
// Arrange

// Act
var result = JsonSerializer.Deserialize<DashboardParticipant>(SINGLE_DASHBOARDPARTICIPANT_JSON, ZoomNetJsonFormatter.SerializerOptions);
var result = JsonSerializer.Deserialize<DashboardParticipant>(SINGLE_DASHBOARDPARTICIPANT_JSON, JsonFormatter.SerializerOptions);

// Assert
result.ShouldNotBeNull();
Expand Down
2 changes: 1 addition & 1 deletion Source/ZoomNet.UnitTests/Models/Registrant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public void Parse_json()
// Arrange

// Act
var result = JsonSerializer.Deserialize<Registrant>(SINGLE_REGISTRANT_JSON, ZoomNetJsonFormatter.SerializerOptions);
var result = JsonSerializer.Deserialize<Registrant>(SINGLE_REGISTRANT_JSON, JsonFormatter.SerializerOptions);

// Assert
result.ShouldNotBeNull();
Expand Down
2 changes: 1 addition & 1 deletion Source/ZoomNet.UnitTests/Resources/CloudRecordingsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ public void Parse_json()
// Arrange

// Act
var result = JsonSerializer.Deserialize<Recording>(SINGLE_CLOUD_RECORDING_JSON, ZoomNetJsonFormatter.SerializerOptions);
var result = JsonSerializer.Deserialize<Recording>(SINGLE_CLOUD_RECORDING_JSON, JsonFormatter.SerializerOptions);

// Assert
result.ShouldNotBeNull();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void Attempt_to_refresh_token_multiple_times_despite_exception()

var mockHttp = new MockHttpMessageHandler();
mockHttp
.When(HttpMethod.Post, $"https://api.zoom.us/oauth/token?grant_type=authorization_code&code={authorizationCode}")
.When(HttpMethod.Post, $"https://api.zoom.us/oauth/token")
.Respond(HttpStatusCode.BadRequest, "application/json", apiResponse);

var handler = new OAuthTokenHandler(connectionInfo, mockHttp.ToHttpClient(), null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public void Read_single(string value, ParticipantDevice expectedValue)

// Act
jsonReader.Read();
var result = converter.Read(ref jsonReader, objectType, ZoomNetJsonFormatter.DeserializerOptions);
var result = converter.Read(ref jsonReader, objectType, JsonFormatter.DeserializerOptions);

// Assert
result.ShouldNotBeNull();
Expand All @@ -109,7 +109,7 @@ public void Read_multiple()

// Act
jsonReader.Read();
var result = converter.Read(ref jsonReader, objectType, ZoomNetJsonFormatter.DeserializerOptions);
var result = converter.Read(ref jsonReader, objectType, JsonFormatter.DeserializerOptions);

// Assert
result.ShouldNotBeNull();
Expand Down
2 changes: 1 addition & 1 deletion Source/ZoomNet.UnitTests/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static Pathoschild.Http.Client.IClient GetFluentClient(MockHttpMessageHan

// Remove all the built-in formatters and replace them with our custom JSON formatter
client.Formatters.Clear();
client.Formatters.Add(new ZoomNetJsonFormatter());
client.Formatters.Add(new JsonFormatter());

// Order is important: DiagnosticHandler must be first.
// Also, the list of filters must be kept in sync with the filters in ZoomClient in the ZoomNet project.
Expand Down
17 changes: 9 additions & 8 deletions Source/ZoomNet.UnitTests/WebhookParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -243,14 +243,15 @@ public void MeetingUpdated()
parsedEvent.Operator.ShouldBe("[email protected]");
parsedEvent.OperatorId.ShouldBe("8lzIwvZTSOqjndWPbPqzuA");
parsedEvent.ModifiedFields.ShouldNotBeNull();
parsedEvent.ModifiedFields.Length.ShouldBe(3);
parsedEvent.ModifiedFields[0].FieldName.ShouldBe("id");
parsedEvent.ModifiedFields[0].OldValue.ShouldBe(94890226305);
parsedEvent.ModifiedFields[0].NewValue.ShouldBe(94890226305);
parsedEvent.ModifiedFields[1].FieldName.ShouldBe("topic");
parsedEvent.ModifiedFields[1].OldValue.ShouldBe("ZoomNet Unit Testing: scheduled meeting");
parsedEvent.ModifiedFields[1].NewValue.ShouldBe("ZoomNet Unit Testing: UPDATED scheduled meeting");
parsedEvent.ModifiedFields[2].FieldName.ShouldBe("settings");
parsedEvent.ModifiedFields.Length.ShouldBe(2);
parsedEvent.ModifiedFields[0].FieldName.ShouldBe("topic");
parsedEvent.ModifiedFields[0].OldValue.ShouldBe("ZoomNet Unit Testing: scheduled meeting");
parsedEvent.ModifiedFields[0].NewValue.ShouldBe("ZoomNet Unit Testing: UPDATED scheduled meeting");
parsedEvent.ModifiedFields[1].FieldName.ShouldBe("settings");
parsedEvent.MeetingFields.ShouldNotBeNull();
parsedEvent.MeetingFields.Length.ShouldBe(1);
parsedEvent.MeetingFields[0].FieldName.ShouldBe("id");
parsedEvent.MeetingFields[0].Value.ShouldBe(94890226305);
}

[Fact]
Expand Down
143 changes: 79 additions & 64 deletions Source/ZoomNet/Extensions/Internal.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using HttpMultipartParser;
using Pathoschild.Http.Client;
using Pathoschild.Http.Client.Extensibility;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
Expand Down Expand Up @@ -227,6 +228,19 @@ internal static Encoding GetEncoding(this HttpContent content, Encoding defaultE
return encoding;
}

/// <summary>
/// Returns the value of a parameter or the default value if it doesn't exist.
/// </summary>
/// <param name="parser">The parser.</param>
/// <param name="name">The name of the parameter.</param>
/// <param name="defaultValue">The default value.</param>
/// <returns>The value of the parameter.</returns>
internal static string GetParameterValue(this MultipartFormDataParser parser, string name, string defaultValue)
{
if (parser.HasParameter(name)) return parser.GetParameterValue(name);
else return defaultValue;
}

/// <summary>Asynchronously retrieve the JSON encoded response body and convert it to an object of the desired type.</summary>
/// <typeparam name="T">The response model to deserialize into.</typeparam>
/// <param name="response">The response.</param>
Expand Down Expand Up @@ -352,59 +366,36 @@ internal static async Task<JsonDocument> AsRawJsonDocument(this IRequest request
/// <returns>Returns the request builder for chaining.</returns>
internal static IRequest WithHttp200TreatedAsFailure(this IRequest request, string customExceptionMessage = null)
{
return request.WithFilter(new ZoomErrorHandler(true, customExceptionMessage));
return request
.WithoutFilter<ZoomErrorHandler>()
.WithFilter(new ZoomErrorHandler(true, customExceptionMessage));
}

/// <summary>Set the body content of the HTTP request.</summary>
/// <typeparam name="T">The type of object to serialize into a JSON string.</typeparam>
/// <param name="request">The request.</param>
/// <param name="body">The value to serialize into the HTTP body content.</param>
/// <param name="omitCharSet">Indicates if the charset should be omitted from the 'Content-Type' request header.</param>
/// <returns>Returns the request builder for chaining.</returns>
/// <remarks>
/// This method is equivalent to IRequest.AsBody&lt;T&gt;(T body) because omitting the media type
/// causes the first formatter in MediaTypeFormatterCollection to be used by default and the first
/// formatter happens to be the JSON formatter. However, I don't feel good about relying on the
/// default ordering of the items in the MediaTypeFormatterCollection.
/// </remarks>
internal static IRequest WithJsonBody<T>(this IRequest request, T body)
{
return request.WithBody(bodyBuilder => bodyBuilder.Model(body, new MediaTypeHeaderValue("application/json")));
}

/// <summary>Add a filter to a request.</summary>
/// <typeparam name="TFilter">The type of filter.</typeparam>
/// <param name="request">The request.</param>
/// <param name="filter">The filter.</param>
/// <param name="replaceExisting">
/// When true, the first filter of matching type is replaced with the new filter (thereby preserving the position of the filter in the list of filters) and any other filter of matching type is removed.
/// When false, the filter is simply added to the list of filters.
/// </param>
/// <returns>Returns the request builder for chaining.</returns>
internal static IRequest WithFilter<TFilter>(this IRequest request, TFilter filter, bool replaceExisting = true)
where TFilter : IHttpFilter
internal static IRequest WithJsonBody<T>(this IRequest request, T body, bool omitCharSet = false)
{
var matchingFilters = request.Filters.OfType<TFilter>().ToArray();

if (matchingFilters.Length == 0 || !replaceExisting)
return request.WithBody(bodyBuilder =>
{
request.Filters.Add(filter);
}
else
{
// Replace the first matching filter with the new filter
var collectionAsList = request.Filters as IList<IHttpFilter>;
var indexOfMatchingFilter = collectionAsList.IndexOf(matchingFilters[0]);
collectionAsList.RemoveAt(indexOfMatchingFilter);
collectionAsList.Insert(indexOfMatchingFilter, filter);

// Remove any other matching filter
for (int i = 1; i < matchingFilters.Length; i++)
var httpContent = bodyBuilder.Model(body, new MediaTypeHeaderValue("application/json"));
if (omitCharSet && !string.IsNullOrEmpty(httpContent.Headers.ContentType.CharSet))
{
request.Filters.Remove(matchingFilters[i]);
httpContent.Headers.ContentType.CharSet = string.Empty;
}
}
return request;
return httpContent;
});
}

/// <summary>Asynchronously retrieve the response body as a <see cref="string"/>.</summary>
Expand Down Expand Up @@ -660,9 +651,6 @@ internal static (WeakReference<HttpRequestMessage> RequestReference, string Diag

internal static async Task<(bool, string, int?)> GetErrorMessageAsync(this HttpResponseMessage message)
{
// Assume there is no error
var isError = false;

// Default error code
int? errorCode = null;

Expand Down Expand Up @@ -696,33 +684,61 @@ internal static (WeakReference<HttpRequestMessage> RequestReference, string Diag
try
{
var rootJsonElement = JsonDocument.Parse(responseContent).RootElement;
errorCode = rootJsonElement.TryGetProperty("code", out JsonElement jsonErrorCode) ? (int?)jsonErrorCode.GetInt32() : (int?)null;
errorMessage = rootJsonElement.TryGetProperty("message", out JsonElement jsonErrorMessage) ? jsonErrorMessage.GetString() : (errorCode.HasValue ? $"Error code: {errorCode}" : errorMessage);
if (rootJsonElement.TryGetProperty("errors", out JsonElement jsonErrorDetails))

if (rootJsonElement.ValueKind == JsonValueKind.Object)
{
var errorDetails = string.Join(
" ",
jsonErrorDetails
.EnumerateArray()
.Select(jsonErrorDetail =>
{
var errorDetail = jsonErrorDetail.TryGetProperty("message", out JsonElement jsonErrorMessage) ? jsonErrorMessage.GetString() : string.Empty;
return errorDetail;
})
.Where(errorDetail => !string.IsNullOrEmpty(errorDetail)));

if (!string.IsNullOrEmpty(errorDetails)) errorMessage += $" {errorDetails}";
}
errorCode = rootJsonElement.TryGetProperty("code", out JsonElement jsonErrorCode) ? (int?)jsonErrorCode.GetInt32() : (int?)null;
errorMessage = rootJsonElement.TryGetProperty("message", out JsonElement jsonErrorMessage) ? jsonErrorMessage.GetString() : (errorCode.HasValue ? $"Error code: {errorCode}" : errorMessage);
if (rootJsonElement.TryGetProperty("errors", out JsonElement jsonErrorDetails))
{
var errorDetails = string.Join(
" ",
jsonErrorDetails
.EnumerateArray()
.Select(jsonErrorDetail =>
{
var errorDetail = jsonErrorDetail.TryGetProperty("message", out JsonElement jsonErrorMessage) ? jsonErrorMessage.GetString() : string.Empty;
return errorDetail;
})
.Where(message => !string.IsNullOrEmpty(message)));

isError = errorCode.HasValue;
if (!string.IsNullOrEmpty(errorDetails)) errorMessage += $" {errorDetails}";
}

return (errorCode.HasValue, errorMessage, errorCode);
}
}
catch
{
// Intentionally ignore parsing errors
}
}

return (isError, errorMessage, errorCode);
return (!message.IsSuccessStatusCode, errorMessage, errorCode);
}

internal static async Task<Stream> CompressAsync(this Stream source)
{
var compressedStream = new MemoryStream();
using (var gzip = new GZipStream(compressedStream, CompressionMode.Compress, true))
{
await source.CopyToAsync(gzip).ConfigureAwait(false);
}

compressedStream.Position = 0;
return compressedStream;
}

internal static async Task<Stream> DecompressAsync(this Stream source)
{
var decompressedStream = new MemoryStream();
using (var gzip = new GZipStream(source, CompressionMode.Decompress, true))
{
await gzip.CopyToAsync(decompressedStream).ConfigureAwait(false);
}

decompressedStream.Position = 0;
return decompressedStream;
}

/// <summary>Convert an enum to its string representation.</summary>
Expand Down Expand Up @@ -821,7 +837,7 @@ internal static bool TryToEnum<T>(this string str, out T enumValue)

internal static T ToObject<T>(this JsonElement element, JsonSerializerOptions options = null)
{
return JsonSerializer.Deserialize<T>(element, options ?? ZoomNetJsonFormatter.DeserializerOptions);
return JsonSerializer.Deserialize<T>(element.GetRawText(), options ?? JsonFormatter.DeserializerOptions);
}

internal static void Add<T>(this JsonObject jsonObject, string propertyName, T value)
Expand Down Expand Up @@ -865,14 +881,13 @@ private static async Task<T> AsObject<T>(this HttpContent httpContent, string pr

if (string.IsNullOrEmpty(propertyName))
{
return JsonSerializer.Deserialize<T>(responseContent, options ?? ZoomNetJsonFormatter.DeserializerOptions);
return JsonSerializer.Deserialize<T>(responseContent, options ?? JsonFormatter.DeserializerOptions);
}

var jsonDoc = JsonDocument.Parse(responseContent, (JsonDocumentOptions)default);
if (jsonDoc.RootElement.TryGetProperty(propertyName, out JsonElement property))
{
var propertyContent = property.GetRawText();
return JsonSerializer.Deserialize<T>(propertyContent, options ?? ZoomNetJsonFormatter.DeserializerOptions);
return property.ToObject<T>(options);
}
else if (throwIfPropertyIsMissing)
{
Expand Down Expand Up @@ -951,7 +966,7 @@ private static async Task<PaginatedResponse<T>> AsPaginatedResponse<T>(this Http
PageCount = pageCount,
PageNumber = pageNumber,
PageSize = pageSize,
Records = jsonProperty.HasValue ? JsonSerializer.Deserialize<T[]>(jsonProperty.Value, options ?? ZoomNetJsonFormatter.DeserializerOptions) : Array.Empty<T>()
Records = jsonProperty.HasValue ? jsonProperty.Value.ToObject<T[]>(options) : Array.Empty<T>()
};
if (totalRecords.HasValue) result.TotalRecords = totalRecords.Value;

Expand Down Expand Up @@ -990,7 +1005,7 @@ private static async Task<PaginatedResponseWithToken<T>> AsPaginatedResponseWith
{
NextPageToken = nextPageToken,
PageSize = pageSize,
Records = jsonProperty.HasValue ? JsonSerializer.Deserialize<T[]>(jsonProperty.Value, options ?? ZoomNetJsonFormatter.DeserializerOptions) : Array.Empty<T>()
Records = jsonProperty.HasValue ? jsonProperty.Value.ToObject<T[]>(options) : Array.Empty<T>()
};
if (totalRecords.HasValue) result.TotalRecords = totalRecords.Value;

Expand Down Expand Up @@ -1033,7 +1048,7 @@ private static async Task<PaginatedResponseWithTokenAndDateRange<T>> AsPaginated
To = to,
NextPageToken = nextPageToken,
PageSize = pageSize,
Records = jsonProperty.HasValue ? JsonSerializer.Deserialize<T[]>(jsonProperty.Value, options ?? ZoomNetJsonFormatter.DeserializerOptions) : Array.Empty<T>()
Records = jsonProperty.HasValue ? jsonProperty.Value.ToObject<T[]>(options) : Array.Empty<T>()
};
if (totalRecords.HasValue) result.TotalRecords = totalRecords.Value;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace ZoomNet.Json
{
internal class ZoomNetJsonFormatter : MediaTypeFormatterBase
internal class JsonFormatter : MediaTypeFormatterBase
{
internal static readonly JsonSerializerOptions SerializerOptions;
internal static readonly JsonSerializerOptions DeserializerOptions;
Expand All @@ -22,7 +22,7 @@ internal class ZoomNetJsonFormatter : MediaTypeFormatterBase

private const int DefaultBufferSize = 1024;

static ZoomNetJsonFormatter()
static JsonFormatter()
{
SerializerOptions = new JsonSerializerOptions()
{
Expand Down Expand Up @@ -64,7 +64,7 @@ static ZoomNetJsonFormatter()
DeserializationContext = new ZoomNetJsonSerializerContext(DeserializerOptions);
}

public ZoomNetJsonFormatter()
public JsonFormatter()
{
this.AddMediaType("application/json");
}
Expand Down
Loading

0 comments on commit c42a165

Please sign in to comment.