Skip to content

Commit

Permalink
v7.3.13
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuel Abraham committed Jul 30, 2023
1 parent 1dbcc2a commit 8efaecc
Show file tree
Hide file tree
Showing 45 changed files with 703 additions and 696 deletions.
76 changes: 76 additions & 0 deletions src/TypeCache.GraphQL/Data/Connection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright (c) 2021 Samuel Abraham

using System.Linq;
using GraphQL.Resolvers;
using GraphQL.Types;
using TypeCache.GraphQL.Attributes;
using TypeCache.GraphQL.Extensions;
using TypeCache.GraphQL.Types;
using static System.FormattableString;

namespace TypeCache.GraphQL.Data;

public class Connection<T>
where T : notnull
{
public Connection()
{
}

public Connection(uint offset, T[] items)
{
offset += 1;
this.Items = items;
this.Edges = items.Select((row, i) => new Edge<T>((int)offset + i, row)).ToArray();
}

[GraphQLDescription("The total number of records available. Returns `null` if the total number is unknown.")]
public int? TotalCount { get; init; }

[GraphQLDescription("Pagination information for this result data set.")]
public PageInfo? PageInfo { get; init; }

[GraphQLDescription("The result data set, stored as a list of edges containing a node (the data) and a cursor (a unique identifier for the data).")]
public Edge<T>[]? Edges { get; init; }

[GraphQLDescription("The result data set.")]
public T[]? Items { get; init; }

public static ObjectGraphType CreateGraphType(string name, IGraphType dataGraphType)
{
var graphType = new ObjectGraphType
{
Name = Invariant($"{name}{nameof(Connection<T>)}"),
Description = typeof(Connection<T>).GraphQLDescription()
};
var edgeGraphType = Edge<T>.CreateGraphType(name, dataGraphType);

graphType.AddField(new()
{
Name = nameof(Connection<T>.Edges),
ResolvedType = new ListGraphType(new NonNullGraphType(edgeGraphType)),
Resolver = new FuncFieldResolver<Connection<T>, Edge<T>[]?>(context => context.Source.Edges)
});
graphType.AddField(new()
{
Name = nameof(Connection<T>.Items),
ResolvedType = new ListGraphType(new NonNullGraphType(dataGraphType)),
Resolver = new FuncFieldResolver<Connection<T>, T[]?>(context => context.Source.Items)
});
graphType.AddField(new()
{
Name = nameof(Connection<T>.PageInfo),
ResolvedType = new GraphQLObjectType<PageInfo>(Invariant($"{name}{nameof(PageInfo)}")),
//Type = typeof(GraphQLObjectType<PageInfo>),
Resolver = new FuncFieldResolver<Connection<T>, PageInfo?>(context => context.Source.PageInfo)
});
graphType.AddField(new()
{
Name = nameof(Connection<T>.TotalCount),
Type = typeof(IntGraphType),
Resolver = new FuncFieldResolver<Connection<T>, int?>(context => context.Source.TotalCount)
});

return graphType;
}
}
40 changes: 40 additions & 0 deletions src/TypeCache.GraphQL/Data/Edge.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) 2021 Samuel Abraham

using GraphQL.Resolvers;
using GraphQL.Types;
using TypeCache.GraphQL.Attributes;
using TypeCache.GraphQL.Extensions;
using static System.FormattableString;

namespace TypeCache.GraphQL.Data;

[GraphQLDescription("An edge in a connection that associates a cursor ID with data.")]
public readonly record struct Edge<T>(
[GraphQLDescription("An identification number for use in pagination.")] int Cursor
, [GraphQLDescription("The data item associated with the cursor value.")] T Node)
where T : notnull
{
public static ObjectGraphType CreateGraphType(string name, IGraphType dataGraphType)
{
var graphType = new ObjectGraphType
{
Name = Invariant($"{name}{nameof(Edge<T>)}"),
Description = typeof(Edge<T>).GraphQLDescription()
};

graphType.AddField(new()
{
Name = nameof(Edge<T>.Cursor),
Type = typeof(IntGraphType),
Resolver = new FuncFieldResolver<Edge<T>, int>(context => context.Source.Cursor)
});
graphType.AddField(new()
{
Name = nameof(Edge<T>.Node),
ResolvedType = new NonNullGraphType(dataGraphType),
Resolver = new FuncFieldResolver<Edge<T>, T>(context => context.Source.Node)
});

return graphType;
}
}
21 changes: 21 additions & 0 deletions src/TypeCache.GraphQL/Data/PageInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) 2021 Samuel Abraham

using GraphQL.Resolvers;
using GraphQL.Types;
using TypeCache.GraphQL.Attributes;
using static System.FormattableString;

namespace TypeCache.GraphQL.Data;

[GraphQLDescription("Information about pagination in a connection.")]
public readonly record struct PageInfo(
[GraphQLDescription("The first cursor value of the current page.")] int StartCursor
, [GraphQLDescription("The last cursor value of the current page.")] int EndCursor
, [GraphQLDescription("Whether a page exists before the current page.")] bool HasPreviousPage
, [GraphQLDescription("Whether a page exists after the current page.")] bool HasNextPage)
{
public PageInfo(uint offset, uint fetch, int totalCount)
: this((int)offset + 1, (int)(offset + fetch), offset > 0, (offset + fetch) < totalCount)
{
}
}
51 changes: 25 additions & 26 deletions src/TypeCache.GraphQL/Extensions/EnumExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,29 @@ namespace TypeCache.GraphQL.Extensions;

public static class EnumExtensions
{
public static Type? ToGraphType(this SystemType @this)
=> @this switch
{
SystemType.String or SystemType.Char or SystemType.Range => typeof(StringGraphType),
SystemType.Uri => typeof(UriGraphType),
SystemType.Boolean => typeof(BooleanGraphType),
SystemType.SByte => typeof(SByteGraphType),
SystemType.Int16 => typeof(ShortGraphType),
SystemType.Int32 or SystemType.Index => typeof(IntGraphType),
SystemType.Int64 or SystemType.IntPtr => typeof(LongGraphType),
SystemType.Int128 or SystemType.UInt128 or SystemType.BigInteger => typeof(BigIntGraphType),
SystemType.Byte => typeof(ByteGraphType),
SystemType.UInt16 => typeof(UShortGraphType),
SystemType.UInt32 => typeof(UIntGraphType),
SystemType.UInt64 or SystemType.UIntPtr => typeof(ULongGraphType),
SystemType.Half => typeof(HalfGraphType),
SystemType.Single or SystemType.Double => typeof(FloatGraphType),
SystemType.Decimal => typeof(DecimalGraphType),
SystemType.DateOnly => typeof(DateOnlyGraphType),
SystemType.DateTime => typeof(DateTimeGraphType),
SystemType.DateTimeOffset => typeof(DateTimeOffsetGraphType),
SystemType.TimeOnly => typeof(TimeOnlyGraphType),
SystemType.TimeSpan => typeof(TimeSpanSecondsGraphType),
SystemType.Guid => typeof(GuidGraphType),
_ => null
};
public static Type? ToGraphType(this SystemType @this) => @this switch
{
SystemType.String or SystemType.Char or SystemType.Range => typeof(StringGraphType),
SystemType.Uri => typeof(UriGraphType),
SystemType.Boolean => typeof(BooleanGraphType),
SystemType.SByte => typeof(SByteGraphType),
SystemType.Int16 => typeof(ShortGraphType),
SystemType.Int32 or SystemType.Index => typeof(IntGraphType),
SystemType.Int64 or SystemType.IntPtr => typeof(LongGraphType),
SystemType.Int128 or SystemType.UInt128 or SystemType.BigInteger => typeof(BigIntGraphType),
SystemType.Byte => typeof(ByteGraphType),
SystemType.UInt16 => typeof(UShortGraphType),
SystemType.UInt32 => typeof(UIntGraphType),
SystemType.UInt64 or SystemType.UIntPtr => typeof(ULongGraphType),
SystemType.Half => typeof(HalfGraphType),
SystemType.Single or SystemType.Double => typeof(FloatGraphType),
SystemType.Decimal => typeof(DecimalGraphType),
SystemType.DateOnly => typeof(DateOnlyGraphType),
SystemType.DateTime => typeof(DateTimeGraphType),
SystemType.DateTimeOffset => typeof(DateTimeOffsetGraphType),
SystemType.TimeOnly => typeof(TimeOnlyGraphType),
SystemType.TimeSpan => typeof(TimeSpanSecondsGraphType),
SystemType.Guid => typeof(GuidGraphType),
_ => null
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ public static bool GraphQLIgnore(this ParameterInfo @this)

public static string GraphQLInputName(this Type @this)
=> @this.GetCustomAttribute<GraphQLInputNameAttribute>()?.Name ?? (@this.IsGenericType
? Invariant($"{@this.Name()}{string.Join(string.Empty, @this.GenericTypeArguments.Select(_ => _.GraphQLName()))}")
? Invariant($"{@this.Name()}{@this.GenericTypeArguments.Select(_ => _.GraphQLName()).Concat()}")
: Invariant($"{@this.GraphQLName()}Input"));

public static string GraphQLName(this MemberInfo @this)
=> @this.GetCustomAttribute<GraphQLNameAttribute>()?.Name ?? @this switch
{
MethodInfo methodInfo => methodInfo.Name().TrimEnd("Async"),
Type type when type.IsGenericType => Invariant($"{type.Name()}{string.Join(string.Empty, type.GenericTypeArguments.Select(_ => _.GraphQLName()))}"),
_ => @this.Name()
MethodInfo methodInfo => methodInfo.Name.TrimEnd("Async"),
Type type when type.IsGenericType => Invariant($"{type.Name()}{type.GenericTypeArguments.Select(_ => _.GraphQLName()).Concat()}"),
_ => @this.Name
};

[MethodImpl(AggressiveInlining), DebuggerHidden]
Expand Down
60 changes: 23 additions & 37 deletions src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using GraphQL;
using GraphQL.Resolvers;
using GraphQL.Types;
using GraphQL.Types.Relay.DataObjects;
using GraphQLParser.AST;
using TypeCache.Extensions;
using TypeCache.GraphQL.Data;
using TypeCache.GraphQL.Resolvers;
using TypeCache.GraphQL.SqlApi;
using TypeCache.GraphQL.Types;
Expand Down Expand Up @@ -51,31 +53,18 @@ public static void AddOrderBy(this EnumerationGraphType @this, OrderBy orderBy,
/// </summary>
/// <typeparam name="T">.</typeparam>
/// <param name="data">The data<see cref="IEnumerable{T}"/>.</param>
/// <param name="totalCount">The total record count of the record set being paged.</param>
/// <param name="offset">The number of records to skip.</param>
/// <param name="totalCount">The total record count of the record set being paged.</param>
/// <returns>The <see cref="Connection{T}"/>.</returns>
public static Connection<T> ToConnection<T>(this IEnumerable<T> data, int totalCount, uint offset)
public static Connection<T> ToConnection<T>(this IEnumerable<T> data, uint offset, int totalCount)
where T : notnull
{
var items = data.AsArray();
var start = offset + 1;
var end = start + items.Length;
var connection = new Connection<T>
return new(offset, items)
{
Edges = items.Select((item, i) => new Edge<T>
{
Cursor = (start + i).ToString(),
Node = item
}).ToList(),
PageInfo = new()
{
StartCursor = start.ToString(),
EndCursor = end.ToString(),
HasNextPage = end < totalCount,
HasPreviousPage = offset > 0
},
PageInfo = new(offset, offset + (uint)items.Length, totalCount),
TotalCount = totalCount
};
return connection;
}

/// <summary>
Expand Down Expand Up @@ -130,39 +119,36 @@ public static Type ToGraphQLType(this Type @this, bool isInputType)
(objectType is ObjectType.Delegate).AssertFalse();
(objectType is ObjectType.Object).AssertFalse();

if (objectType.IsAny(ObjectType.Dictionary, ObjectType.ReadOnlyDictionary))
if (objectType is ObjectType.Dictionary || objectType is ObjectType.ReadOnlyDictionary)
return typeof(KeyValuePair<,>).MakeGenericType(@this.GenericTypeArguments).ToGraphQLType(isInputType).ToNonNullGraphType().ToListGraphType();

if (@this.IsEnum)
return @this.ToGraphQLEnumType();

var systemType = @this.GetSystemType();
if (systemType is SystemType.Task || systemType is SystemType.ValueTask)
return @this.IsGenericType
? @this.GenericTypeArguments.First()!.ToGraphQLType(false)
: throw new ArgumentOutOfRangeException(nameof(@this), Invariant($"{nameof(Task)} and {nameof(ValueTask)} are not allowed as GraphQL types."));

if (systemType is SystemType.Nullable)
return @this.GenericTypeArguments.First()!.ToGraphQLType(isInputType);

var systemGraphType = systemType.ToGraphType();
if (systemGraphType is not null)
return systemGraphType;

if (@this.HasElementType)
{
var elementType = @this.GetElementType()!;
elementType = elementType.GetSystemType() is not SystemType.Nullable && elementType.IsValueType
? elementType.ToGraphQLType(isInputType).ToNonNullGraphType()
: elementType.ToGraphQLType(isInputType);
var elementType = @this.GetElementType()!.ToGraphQLType(isInputType);
if (elementType.IsValueType && elementType.GetSystemType() is not SystemType.Nullable)
elementType = elementType.ToNonNullGraphType();

return elementType.ToListGraphType();
}

if (@this.IsGenericType)
{
@this.GenericTypeArguments.Length.AssertEquals(1);

var genericType = @this.GenericTypeArguments.First()!;
return (systemType, genericType.GetSystemType()) switch
{
(SystemType.Nullable, _) => genericType.GetSystemType().ToGraphType()!,
(SystemType.Task or SystemType.ValueTask, _) => genericType.ToGraphQLType(false),
(_, not SystemType.Nullable) when genericType.IsValueType => genericType.ToGraphQLType(isInputType).ToNonNullGraphType().ToListGraphType(),
_ => genericType.ToGraphQLType(isInputType).ToListGraphType(),
};
}
if (@this.IsGenericType && @this.IsOrImplements(typeof(IEnumerable<>)))
return @this.GenericTypeArguments.First()!.ToGraphQLType(isInputType).ToListGraphType();

if (@this.IsInterface)
return @this.ToGraphQLInterfaceType();
Expand Down
Loading

0 comments on commit 8efaecc

Please sign in to comment.