Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🌿 Fern Regeneration -- August 26, 2024 #47

Merged
merged 2 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/AssemblyAI/Core/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ namespace AssemblyAI.Core;
internal static class Constants
{
public const string DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffK";
public const string DateFormat = "yyyy-MM-dd";
}
14 changes: 14 additions & 0 deletions src/AssemblyAI/Core/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.Runtime.Serialization;

namespace AssemblyAI.Core;

internal static class Extensions
{
public static string Stringify(this Enum value)
{
var field = value.GetType().GetField(value.ToString());
var attribute = (EnumMemberAttribute)
Attribute.GetCustomAttribute(field, typeof(EnumMemberAttribute));
return attribute?.Value ?? value.ToString();
}
}
17 changes: 17 additions & 0 deletions src/AssemblyAI/Core/HeaderValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using OneOf;

namespace AssemblyAI.Core;

internal sealed class HeaderValue(OneOf<string, Func<string>> value)
: OneOfBase<string, Func<string>>(value)
{
public static implicit operator HeaderValue(string value)
{
return new HeaderValue(value);
}

public static implicit operator HeaderValue(Func<string> value)
{
return new HeaderValue(value);
}
}
17 changes: 17 additions & 0 deletions src/AssemblyAI/Core/Headers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace AssemblyAI.Core;

internal sealed class Headers : Dictionary<string, HeaderValue>
{
public Headers() { }

public Headers(Dictionary<string, string> value)
{
foreach (var kvp in value)
{
this[kvp.Key] = new HeaderValue(kvp.Value);
}
}

public Headers(IEnumerable<KeyValuePair<string, HeaderValue>> value)
: base(value.ToDictionary(e => e.Key, e => e.Value)) { }
}
9 changes: 2 additions & 7 deletions src/AssemblyAI/Core/Public/AssemblyAIException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,5 @@ namespace AssemblyAI;
/// <summary>
/// Base exception class for all exceptions thrown by the SDK.
/// </summary>
public class AssemblyAIException : Exception
{
internal AssemblyAIException(string message, Exception? innerException = null)
: base(message, innerException)
{
}
}
public class AssemblyAIException(string message, Exception? innerException = null)
: Exception(message, innerException) { }
28 changes: 12 additions & 16 deletions src/AssemblyAI/Core/Public/ClientOptions.cs
Original file line number Diff line number Diff line change
@@ -1,39 +1,35 @@
using System;
using System.Net.Http;
// ReSharper disable PartialTypeWithSinglePart
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable CheckNamespace
using AssemblyAI.Core;

#nullable enable

namespace AssemblyAI;

public partial class ClientOptions
{
/// <summary>
/// The AssemblyAI API key
/// </summary>
public required string ApiKey { get; set; }

/// <summary>
/// The Base URL for the API.
/// </summary>
public string BaseUrl { get; set; } = AssemblyAIClientEnvironment.Default;
public string BaseUrl { get; init; } = AssemblyAIClientEnvironment.Default;

/// <summary>
/// The AssemblyAI user agent
/// The http client used to make requests.
/// </summary>
public UserAgent UserAgent { get; set; } = new();
public HttpClient HttpClient { get; init; } = new HttpClient();

/// <summary>
/// The http client used to make requests.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doc comment is incorrect.

/// </summary>
public HttpClient? HttpClient { get; set; }
public int MaxRetries { get; init; } = 2;

/// <summary>
/// The http client used to make requests.
/// The timeout for the request.
/// </summary>
public int MaxRetries { get; set; } = 2;
public TimeSpan Timeout { get; init; } = TimeSpan.FromSeconds(30);

/// <summary>
/// The timeout for the request.
/// The http headers sent with the request.
/// </summary>
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30);
internal Headers Headers { get; init; } = new();
}
6 changes: 6 additions & 0 deletions src/AssemblyAI/Core/Public/RequestOptions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Net.Http;
using AssemblyAI.Core;

#nullable enable

Expand All @@ -26,4 +27,9 @@ public partial class RequestOptions
/// The timeout for the request.
/// </summary>
public TimeSpan? Timeout { get; init; }

/// <summary>
/// The http headers sent with the request.
/// </summary>
internal Headers Headers { get; init; } = new();
}
52 changes: 24 additions & 28 deletions src/AssemblyAI/Core/RawClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Net.Http;
using System.Text;
using System.Threading;

namespace AssemblyAI.Core;

Expand All @@ -8,45 +9,28 @@ namespace AssemblyAI.Core;
/// <summary>
/// Utility class for making raw HTTP requests to the API.
/// </summary>
internal class RawClient(
Dictionary<string, string> headers,
Dictionary<string, Func<string>> headerSuppliers,
ClientOptions clientOptions
)
internal class RawClient(ClientOptions clientOptions)
{
/// <summary>
/// The http client used to make requests.
/// </summary>
public readonly ClientOptions Options = clientOptions;

/// <summary>
/// Global headers to be sent with every request.
/// </summary>
private readonly Dictionary<string, string> _headers = headers;

public async Task<ApiResponse> MakeRequestAsync(BaseApiRequest request)
public async Task<ApiResponse> MakeRequestAsync(
BaseApiRequest request,
CancellationToken cancellationToken = default
)
{
var url = BuildUrl(request);
var httpRequest = new HttpRequestMessage(request.Method, url);
if (request.ContentType != null)
{
request.Headers.Add("Content-Type", request.ContentType);
}
// Add global headers to the request
foreach (var header in _headers)
{
httpRequest.Headers.Add(header.Key, header.Value);
}
// Add global headers to the request from supplier
foreach (var header in headerSuppliers)
{
httpRequest.Headers.Add(header.Key, header.Value.Invoke());
}
// Add request headers to the request
foreach (var header in request.Headers)
{
httpRequest.Headers.Add(header.Key, header.Value);
}
SetHeaders(httpRequest, Options.Headers);
SetHeaders(httpRequest, request.Headers);
SetHeaders(httpRequest, request.Options?.Headers ?? new());

// Add the request body to the request
if (request is JsonApiRequest jsonRequest)
{
Expand All @@ -65,7 +49,7 @@ public async Task<ApiResponse> MakeRequestAsync(BaseApiRequest request)
}
// Send the request
var httpClient = request.Options?.HttpClient ?? Options.HttpClient;
var response = await httpClient.SendAsync(httpRequest);
var response = await httpClient.SendAsync(httpRequest, cancellationToken);
return new ApiResponse { StatusCode = (int)response.StatusCode, Raw = response };
}

Expand All @@ -81,7 +65,7 @@ public record BaseApiRequest

public Dictionary<string, object> Query { get; init; } = new();

public Dictionary<string, string> Headers { get; init; } = new();
public Headers Headers { get; init; } = new();

public RequestOptions? Options { get; init; }
}
Expand Down Expand Up @@ -112,6 +96,18 @@ public record ApiResponse
public required HttpResponseMessage Raw { get; init; }
}

private void SetHeaders(HttpRequestMessage httpRequest, Headers headers)
{
foreach (var header in headers)
{
var value = header.Value?.Match(str => str, func => func.Invoke());
if (value != null)
{
httpRequest.Headers.TryAddWithoutValidation(header.Key, value);
}
}
}

private string BuildUrl(BaseApiRequest request)
{
var baseUrl = request.Options?.BaseUrl ?? request.BaseUrl;
Expand Down
12 changes: 9 additions & 3 deletions src/AssemblyAI/Files/FilesClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using AssemblyAI;
using AssemblyAI.Core;

Expand All @@ -19,7 +20,11 @@ internal FilesClient(RawClient client)
/// <summary>
/// Upload a media file to AssemblyAI's servers.
/// </summary>
public async Task<UploadedFile> UploadAsync(Stream request, RequestOptions? options = null)
public async Task<UploadedFile> UploadAsync(
Stream request,
RequestOptions? options = null,
CancellationToken cancellationToken = default
)
{
var response = await _client.MakeRequestAsync(
new RawClient.StreamApiRequest
Expand All @@ -28,8 +33,9 @@ public async Task<UploadedFile> UploadAsync(Stream request, RequestOptions? opti
Method = HttpMethod.Post,
Path = "v2/upload",
Body = request,
Options = options
}
Options = options,
},
cancellationToken
);
var responseBody = await response.Raw.Content.ReadAsStringAsync();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cancellationToken should be passed to response.Raw.Content.ReadAsStringAsync too

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anything that's async usually

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also .ConfigureAwait(false)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll need to use conditional compilation tho:

#if NET6_0_OR_GREATER
        var responseBody = await response.Raw.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
#else
        var responseBody = await response.Raw.Content.ReadAsStringAsync().ConfigureAwait(false);
#endif

if (response.StatusCode is >= 200 and < 400)
Expand Down
6 changes: 6 additions & 0 deletions src/AssemblyAI/Files/Types/UploadedFile.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using AssemblyAI.Core;

#nullable enable

Expand All @@ -11,4 +12,9 @@ public record UploadedFile
/// </summary>
[JsonPropertyName("upload_url")]
public required string UploadUrl { get; set; }

public override string ToString()
{
return JsonUtils.Serialize(this);
}
}
Loading
Loading