Skip to content

Commit

Permalink
v8.4.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuel Abraham committed Nov 26, 2024
1 parent d248801 commit f7f413f
Show file tree
Hide file tree
Showing 24 changed files with 460 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,7 @@ namespace TypeCache.GraphQL.Attributes;
/// Sets the deprecation reason of the object type, object property, enum type, enum field, endpoint or endpoint parameter.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Struct)]
public sealed class GraphQLDeprecationReasonAttribute : Attribute
public sealed class GraphQLDeprecationReasonAttribute(string deprecationReason) : Attribute
{
public GraphQLDeprecationReasonAttribute(string deprecationReason)
{
deprecationReason.ThrowIfBlank();

this.DeprecationReason = deprecationReason;
}

public string DeprecationReason { get; }
public string DeprecationReason { get; } = deprecationReason;
}
11 changes: 2 additions & 9 deletions src/TypeCache.GraphQL/Attributes/GraphQLDescriptionAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,7 @@ namespace TypeCache.GraphQL.Attributes;
/// Sets the description of the object type, object property, enum type, enum field, endpoint or endpoint parameter.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Struct)]
public sealed class GraphQLDescriptionAttribute : Attribute
public sealed class GraphQLDescriptionAttribute(string description) : Attribute
{
public GraphQLDescriptionAttribute(string description)
{
description.ThrowIfBlank();

this.Description = description;
}

public string Description { get; }
public string Description { get; } = description;
}
12 changes: 2 additions & 10 deletions src/TypeCache.GraphQL/Attributes/GraphQLInputNameAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,7 @@ namespace TypeCache.GraphQL.Attributes;
/// Sets the name of the input type.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public sealed class GraphQLInputNameAttribute : Attribute
public sealed class GraphQLInputNameAttribute(string name) : Attribute
{
/// <exception cref="ArgumentNullException"/>
public GraphQLInputNameAttribute(string name)
{
name.ThrowIfBlank();

this.Name = name;
}

public string Name { get; }
public string Name { get; } = name;
}
12 changes: 2 additions & 10 deletions src/TypeCache.GraphQL/Attributes/GraphQLNameAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,7 @@ namespace TypeCache.GraphQL.Attributes;
/// Sets the name of the object type, object property, enum type, enum field, endpoint or endpoint parameter.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Struct)]
public sealed class GraphQLNameAttribute : Attribute
public sealed class GraphQLNameAttribute(string name) : Attribute
{
/// <exception cref="ArgumentNullException"/>
public GraphQLNameAttribute(string name)
{
name.ThrowIfBlank();

this.Name = name;
}

public string Name { get; }
public string Name { get; } = name;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) 2021 Samuel Abraham

using GraphQL;
using GraphQL.DI;
using GraphQL.Types;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
Expand All @@ -11,6 +12,13 @@ namespace TypeCache.GraphQL.Extensions;

public static class ApplicationBuilderExtensions
{
/// <summary>
/// <c>=&gt; @<paramref name="this"/>.UseMiddleware&lt;<see cref="GraphQLMiddleware"/>&gt;(<paramref name="route"/>, <see langword="new"/> <see cref="ConfigureSchema"/>(<paramref name="configureSchema"/>));</c>
/// </summary>
/// <param name="route">The route to use for this <c><see cref="ISchema"/></c> instance.</param>
public static IApplicationBuilder UseGraphQLSchema(this IApplicationBuilder @this, PathString route, IConfigureSchema configureSchema)
=> @this.UseMiddleware<GraphQLMiddleware>(route, configureSchema);

/// <summary>
/// <c>=&gt; @<paramref name="this"/>.UseMiddleware&lt;<see cref="GraphQLMiddleware"/>&gt;(<paramref name="route"/>, <see langword="new"/> <see cref="ConfigureSchema"/>(<paramref name="configureSchema"/>));</c>
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace TypeCache.GraphQL.Extensions;

public static class GraphQLAttributeExtensions
public static class AttributeExtensions
{
[MethodImpl(AggressiveInlining), DebuggerHidden]
public static string? GraphQLDeprecationReason(this MemberInfo @this)
Expand All @@ -25,7 +25,7 @@ public static class GraphQLAttributeExtensions

[MethodImpl(AggressiveInlining), DebuggerHidden]
public static string? GraphQLDescription(this ParameterInfo @this)
=> @this.GetCustomAttribute<GraphQLDescriptionAttribute>()?.Description;
=> @this.GetCustomAttribute<GraphQLDescriptionAttribute>()?.Description?.NullIfBlank();

[MethodImpl(AggressiveInlining), DebuggerHidden]
public static bool GraphQLIgnore(this MemberInfo @this)
Expand All @@ -36,10 +36,10 @@ public static bool GraphQLIgnore(this ParameterInfo @this)
=> @this.HasCustomAttribute<GraphQLIgnoreAttribute>();

public static string GraphQLInputName(this Type @this)
=> @this.GetCustomAttribute<GraphQLInputNameAttribute>()?.Name ?? Invariant($"{@this.GraphQLName()}Input");
=> @this.GetCustomAttribute<GraphQLInputNameAttribute>()?.Name?.NullIfBlank() ?? Invariant($"{@this.GraphQLName()}Input");

public static string GraphQLName(this MemberInfo @this)
=> @this.GetCustomAttribute<GraphQLNameAttribute>()?.Name ?? @this switch
=> @this.GetCustomAttribute<GraphQLNameAttribute>()?.Name?.NullIfBlank() ?? @this switch
{
MethodInfo methodInfo => methodInfo.Name.TrimEnd("Async"),
Type type when type.IsGenericType => Invariant($"{type.Name()}{type.GenericTypeArguments.Select(_ => _.GraphQLName()).Concat()}"),
Expand All @@ -49,7 +49,7 @@ public static string GraphQLName(this MemberInfo @this)

[MethodImpl(AggressiveInlining), DebuggerHidden]
public static string GraphQLName(this ParameterInfo @this)
=> @this.GetCustomAttribute<GraphQLNameAttribute>()?.Name ?? @this.Name();
=> @this.GetCustomAttribute<GraphQLNameAttribute>()?.Name?.NullIfBlank() ?? @this.Name();

[MethodImpl(AggressiveInlining), DebuggerHidden]
public static Type? GraphQLType(this MemberInfo @this)
Expand Down
4 changes: 2 additions & 2 deletions src/TypeCache.GraphQL/TypeCache.GraphQL.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<Nullable>enable</Nullable>
<RootNamespace>TypeCache.GraphQL</RootNamespace>
<PackageId>TypeCache.GraphQL</PackageId>
<Version>8.4.0</Version>
<Authors>Samuel Abraham &lt;sam987883@gmail.com&gt;</Authors>
<Version>8.4.1</Version>
<Authors>Samuel Abraham &lt;sam987883@gmail.com&gt;</Authors>
<Company>Samuel Abraham &lt;sam987883@gmail.com&gt;</Company>
<Title>TypeCache GraphQL</Title>
<Description>An easier way to add endpoints to GraphQL:
Expand Down
30 changes: 12 additions & 18 deletions src/TypeCache.GraphQL/Web/GraphQLMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,61 +7,55 @@
using GraphQL.Validation;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using TypeCache.Extensions;
using static System.Net.Mime.MediaTypeNames;
using static System.StringSplitOptions;

namespace TypeCache.GraphQL.Web;

public sealed class GraphQLMiddleware
public sealed class GraphQLMiddleware(RequestDelegate next, PathString route, IConfigureSchema configureSchema)
{
private readonly RequestDelegate _Next;
private readonly PathString _Route;
private readonly ISchema _Schema;

public GraphQLMiddleware(RequestDelegate next, PathString route, IConfigureSchema configureSchema, IServiceProvider provider)
{
this._Next = next;
this._Route = route;
this._Schema = new Schema(provider, [configureSchema]);
}

public async Task Invoke(HttpContext httpContext
, IServiceProvider provider
, IDocumentExecuter executer
, IDocumentExecutionListener listener
, IGraphQLSerializer graphQLSerializer
, ILogger<GraphQLMiddleware> logger)
{
if (!httpContext.Request.Path.Equals(this._Route))
if (!httpContext.Request.Path.Equals(route))
{
await this._Next.Invoke(httpContext);
await next.Invoke(httpContext);
return;
}

var request = await graphQLSerializer.ReadAsync<GraphQLRequest>(httpContext.Request.Body, httpContext.RequestAborted);
if (request is null)
{
await this._Next.Invoke(httpContext);
await next.Invoke(httpContext);
return;
}

var requestId = Guid.NewGuid();
var requestTime = DateTime.UtcNow;
var timeProvider = provider.GetService(typeof(TimeProvider)) as TimeProvider ?? TimeProvider.System;
var requestTime = timeProvider.GetLocalNow().ToISO8601();
var userContext = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
{
{ "RequestId", requestId },
{ "RequestTime", requestTime },
{ nameof(httpContext.User), httpContext.User }
};
this._Schema.Description = Invariant($"GraphQL schema route: {this._Route}");
var schema = new Schema(provider, [configureSchema])
{
Description = "GraphQL schema route: " + route
};
var options = new ExecutionOptions
{
CancellationToken = httpContext.RequestAborted,
Variables = request.Variables is not null ? new Inputs(request.Variables!) : null,
OperationName = request.OperationName,
Query = request.Query,
RequestServices = provider,
Schema = this._Schema,
Schema = schema,
UserContext = userContext,
ValidationRules = DocumentValidator.CoreRules
};
Expand Down
4 changes: 2 additions & 2 deletions src/TypeCache.Web/Mediation/HttpClientRule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ public async Task<HttpResponseMessage> Map(HttpClientRequest request, Cancellati

var logLevel = httpResponse.IsSuccessStatusCode ? LogLevel.Information : LogLevel.Error;
logger?.Log(logLevel, Invariant($"{{{nameof(request.Message.Method)}}} {{{nameof(request.Message.RequestUri)}}} - {{{nameof(httpResponse.StatusCode)}}}"),
[request.Message.Method.Method, request.Message.RequestUri, httpResponse.StatusCode]);
request.Message.Method.Method, request.Message.RequestUri, httpResponse.StatusCode);

return httpResponse;
}
catch (Exception error)
{
logger?.LogError(error, Invariant($"{{{nameof(request.Message.Method)}}} {{{nameof(request.Message.RequestUri)}}} - {{ErrorMessage}}"),
[request.Message.Method.Method, request.Message.RequestUri, error.Message]);
request.Message.Method.Method, request.Message.RequestUri, error.Message);

throw;
}
Expand Down
2 changes: 1 addition & 1 deletion src/TypeCache.Web/TypeCache.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<Nullable>enable</Nullable>
<RootNamespace>TypeCache.Web</RootNamespace>
<PackageId>TypeCache.Web</PackageId>
<Version>8.3.1</Version>
<Version>8.4.1</Version>
<Authors>Samuel Abraham &lt;sam987883@gmail.com&gt;</Authors>
<Company>Samuel Abraham &lt;sam987883@gmail.com&gt;</Company>
<Title>TypeCache Web Library</Title>
Expand Down
49 changes: 49 additions & 0 deletions src/TypeCache/Extensions/ActionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) 2021 Samuel Abraham

using TypeCache.Utilities;

namespace TypeCache.Extensions;

public static class ActionExtensions
{
/// <summary>
/// Retry a failed <see cref="Action"/>. The # of <c><paramref name="retryDelays"/></c> dictates the # of retry attempts.<br/>
/// Some built-in interval sequences to use for retry delays:<br/>
/// <list type="table">
/// <item><c><see cref="Sequence.ExponentialSeconds()"/></c></item>
/// <item><c><see cref="Sequence.ExponentialSeconds(uint)"/></c></item>
/// <item><c><see cref="Sequence.LinearTime(TimeSpan)"/></c></item>
/// </list>
/// These are increasing infinite sequences, hence an infinite # of retries will be attempted.<br/>
/// To limit the number of retries, call Linq's Take(...) method on the returned collection.
/// </summary>
public static async Task Retry(this Action @this, IEnumerable<TimeSpan> retryDelays, TimeProvider? timeProvider = default, CancellationToken token = default)
{
@this.ThrowIfNull();

try
{
await Task.Run(@this, token);
}
catch (Exception lastError)
{
timeProvider ??= TimeProvider.System;

foreach (var delay in retryDelays)
{
await Task.Delay(delay, timeProvider, token);
try
{
await Task.Run(@this, token);
return;
}
catch (Exception ex)
{
lastError = ex;
}
}

await Task.FromException(lastError);
}
}
}
46 changes: 44 additions & 2 deletions src/TypeCache/Extensions/ArrayExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ public static void ForEach<T>(this T[]? @this, ActionRef<T> action)

public static void ForEach<T>(this T[] @this, Action<T, int> action)
{
var i = -1;
@this.ForEach(value => action(value, ++i));
for (var i = 0; i < @this.Length; ++i)
action(@this[i], i);
}

/// <exception cref="ArgumentNullException"/>
Expand Down Expand Up @@ -107,6 +107,48 @@ public static void ForEach<T>(this T[] @this, Action<T, int> action, Action betw
});
}

/// <exception cref="ArgumentNullException"/>
[MethodImpl(AggressiveInlining), DebuggerHidden]
public static async Task ForEachAsync<T>(this T[] @this, Action<T> action, CancellationToken token = default)
{
@this.ThrowIfNull();
action.ThrowIfNull();

for (var i = 0; i < @this.Length; ++i)
{
if (token.IsCancellationRequested)
{
await Task.FromCanceled(token);
return;
}

action(@this[i]);
}

await Task.CompletedTask;
}

/// <exception cref="ArgumentNullException"/>
[MethodImpl(AggressiveInlining), DebuggerHidden]
public static async Task ForEachAsync<T>(this T[] @this, Action<T, int> action, CancellationToken token = default)
{
@this.ThrowIfNull();
action.ThrowIfNull();

for (var i = 0; i < @this.Length; ++i)
{
if (token.IsCancellationRequested)
{
await Task.FromCanceled(token);
return;
}

action(@this[i], i);
}

await Task.CompletedTask;
}

/// <inheritdoc cref="Convert.FromBase64CharArray(char[], int, int)"/>
/// <remarks>
/// <c>=&gt; <see cref="Convert"/>.FromBase64CharArray(@<paramref name="this"/>, 0, @<paramref name="this"/>.Length);</c>
Expand Down
45 changes: 45 additions & 0 deletions src/TypeCache/Extensions/FuncExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) 2021 Samuel Abraham

namespace TypeCache.Extensions;

public static class FuncExtensions
{
/// <summary>
/// Retry a failed <see cref="Func{TResult}"/>. The # of <c><paramref name="retryDelays"/></c> dictates the # of retry attempts.<br/>
/// Some built-in interval sequences to use for retry delays:<br/>
/// <list type="table">
/// <item><c><see cref="Sequence.ExponentialSeconds()"/></c></item>
/// <item><c><see cref="Sequence.ExponentialSeconds(uint)"/></c></item>
/// <item><c><see cref="Sequence.LinearTime(TimeSpan)"/></c></item>
/// </list>
/// These are increasing infinite sequences, hence an infinite # of retries will be attempted.<br/>
/// To limit the number of retries, call Linq's Take(...) method on the returned collection.
/// </summary>
public static async Task<T> Retry<T>(this Func<T> @this, IEnumerable<TimeSpan> retryDelays, TimeProvider? timeProvider = default, CancellationToken token = default)
{
@this.ThrowIfNull();

try
{
return await Task.Run(@this, token);
}
catch (Exception lastError)
{
timeProvider ??= TimeProvider.System;
foreach (var delay in retryDelays)
{
await Task.Delay(delay, timeProvider, token);
try
{
return await Task.Run(@this, token);
}
catch (Exception ex)
{
lastError = ex;
}
}

return await Task.FromException<T>(lastError);
}
}
}
Loading

0 comments on commit f7f413f

Please sign in to comment.