Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
53 changes: 29 additions & 24 deletions src/HotChocolate/Core/src/Types.CursorPagination/ConnectionType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,23 @@ internal ConnectionType(
string connectionName,
TypeReference nodeType,
bool includeTotalCount,
bool includeNodesField)
bool includeNodesField,
INamingConventions namingConventions)
{
ArgumentException.ThrowIfNullOrEmpty(connectionName);
ArgumentNullException.ThrowIfNull(nodeType);

ConnectionName = connectionName;
var edgeTypeName = NameHelper.CreateEdgeName(connectionName);
var edgeTypeName = NameHelper.CreateEdgeName(namingConventions, connectionName);

var edgesType =
TypeReference.Parse(
$"[{edgeTypeName}!]",
TypeContext.Output,
factory: _ => new EdgeType(connectionName, nodeType));
factory: c => new EdgeType(connectionName, nodeType, c.Naming));

Configuration = CreateConfiguration(includeTotalCount, includeNodesField, edgesType);
Configuration.Name = NameHelper.CreateConnectionName(connectionName);
Configuration.Name = NameHelper.CreateConnectionName(namingConventions, connectionName);
Configuration.Dependencies.Add(new TypeDependency(nodeType));
Configuration.Tasks.Add(
new OnCompleteTypeSystemConfigurationTask(
Expand Down Expand Up @@ -70,7 +71,7 @@ internal ConnectionType(TypeReference nodeType, bool includeTotalCount, bool inc
TypeReference.Create(
ContextDataKeys.EdgeType,
nodeType,
_ => new EdgeType(nodeType),
c => new EdgeType(nodeType, c.Naming),
TypeContext.Output);

// the property is set later in the configuration
Expand All @@ -90,9 +91,9 @@ internal ConnectionType(TypeReference nodeType, bool includeTotalCount, bool inc
var definition = (ObjectTypeConfiguration)d;
var edges = definition.Fields.First(IsEdgesField);

definition.Name = NameHelper.CreateConnectionName(ConnectionName);
definition.Name = NameHelper.CreateConnectionName(c.DescriptorContext.Naming, ConnectionName);
edges.Type = TypeReference.Parse(
$"[{NameHelper.CreateEdgeName(ConnectionName)}!]",
$"[{NameHelper.CreateEdgeName(c.DescriptorContext.Naming, ConnectionName)}!]",
TypeContext.Output);

if (includeNodesField)
Expand Down Expand Up @@ -130,11 +131,13 @@ protected override void OnBeforeRegisterDependencies(
ITypeDiscoveryContext context,
TypeSystemConfiguration configuration)
{
context.Dependencies.Add(new TypeDependency(context.TypeInspector.GetOutputTypeRef(typeof(PageInfoType))));
context.Dependencies.Add(new TypeDependency(
context.TypeInspector.GetOutputTypeRef(typeof(PageInfoType))));

if (context.DescriptorContext.Options.ApplyShareableToConnections)
{
context.Dependencies.Add(new TypeDependency(context.TypeInspector.GetOutputTypeRef(typeof(Shareable))));
context.Dependencies.Add(new TypeDependency(
context.TypeInspector.GetOutputTypeRef(typeof(Shareable))));

var config = (ObjectTypeConfiguration)configuration;
config.AddDirective(Shareable.Instance, context.TypeInspector);
Expand Down Expand Up @@ -184,25 +187,27 @@ private static ObjectTypeConfiguration CreateConfiguration(

if (includeNodesField)
{
definition.Fields.Add(new ObjectFieldConfiguration(
Names.Nodes,
ConnectionType_Nodes_Description,
pureResolver: GetNodes)
{
Flags = CoreFieldFlags.ConnectionNodesField
});
definition.Fields.Add(
new ObjectFieldConfiguration(
Names.Nodes,
ConnectionType_Nodes_Description,
pureResolver: GetNodes)
{
Flags = CoreFieldFlags.ConnectionNodesField
});
}

if (includeTotalCount)
{
definition.Fields.Add(new ObjectFieldConfiguration(
Names.TotalCount,
ConnectionType_TotalCount_Description,
type: TypeReference.Parse($"{ScalarNames.Int}!"),
pureResolver: GetTotalCount)
{
Flags = CoreFieldFlags.TotalCount
});
definition.Fields.Add(
new ObjectFieldConfiguration(
Names.TotalCount,
ConnectionType_TotalCount_Description,
type: TypeReference.Parse($"{ScalarNames.Int}!"),
pureResolver: GetTotalCount)
{
Flags = CoreFieldFlags.TotalCount
});
}

return definition;
Expand Down
9 changes: 5 additions & 4 deletions src/HotChocolate/Core/src/Types.CursorPagination/EdgeType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,23 @@ internal sealed class EdgeType : ObjectType, IEdgeType
{
internal EdgeType(
string connectionName,
TypeReference nodeType)
TypeReference nodeType,
INamingConventions namingConventions)
{
ArgumentException.ThrowIfNullOrEmpty(connectionName);
ArgumentNullException.ThrowIfNull(nodeType);

ConnectionName = connectionName;
Configuration = CreateConfiguration(nodeType);
Configuration.Name = NameHelper.CreateEdgeName(connectionName);
Configuration.Name = NameHelper.CreateEdgeName(namingConventions, connectionName);
Configuration.Tasks.Add(
new OnCompleteTypeSystemConfigurationTask(
(c, _) => NodeType = c.GetType<IOutputType>(nodeType),
Configuration,
ApplyConfigurationOn.BeforeCompletion));
}

internal EdgeType(TypeReference nodeType)
internal EdgeType(TypeReference nodeType, INamingConventions namingConventions)
{
ArgumentNullException.ThrowIfNull(nodeType);

Expand All @@ -43,7 +44,7 @@ internal EdgeType(TypeReference nodeType)
{
var type = c.GetType<IType>(nodeType);
ConnectionName = type.NamedType().Name;
((ObjectTypeConfiguration)d).Name = NameHelper.CreateEdgeName(ConnectionName);
((ObjectTypeConfiguration)d).Name = NameHelper.CreateEdgeName(namingConventions, ConnectionName);
},
Configuration,
ApplyConfigurationOn.BeforeNaming,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ private static TypeReference CreateConnectionTypeRef(
// last but not least we create a type reference that can be put on the field definition
// to tell the type discovery that this field needs this result type.
return CreateConnectionType(
context,
connectionName,
nodeType,
options.IncludeTotalCount ?? false,
Expand Down Expand Up @@ -425,6 +426,7 @@ providerName is null
}

private static TypeReference CreateConnectionType(
IDescriptorContext context,
string? connectionName,
TypeReference nodeType,
bool includeTotalCount,
Expand All @@ -437,13 +439,14 @@ private static TypeReference CreateConnectionType(
_ => new ConnectionType(nodeType, includeTotalCount, includeNodesField),
TypeContext.Output)
: TypeReference.Create(
connectionName + "Connection",
NameHelper.CreateConnectionName(context.Naming, connectionName),
TypeContext.Output,
factory: _ => new ConnectionType(
factory: c => new ConnectionType(
connectionName,
nodeType,
includeTotalCount,
includeNodesField));
includeNodesField,
c.Naming));
}

private static string EnsureConnectionNameCasing(string connectionName)
Expand Down
14 changes: 10 additions & 4 deletions src/HotChocolate/Core/src/Types.CursorPagination/NameHelper.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
using HotChocolate.Types.Descriptors;

namespace HotChocolate.Types.Pagination;

internal static class NameHelper
{
public static string CreateConnectionName(string connectionName)
=> connectionName + "Connection";
public static string CreateConnectionName(
INamingConventions namingConventions,
string connectionName)
=> namingConventions.GetTypeName(connectionName + "Connection", TypeKind.Object);

public static string CreateEdgeName(string connectionName)
=> connectionName + "Edge";
public static string CreateEdgeName(
INamingConventions namingConventions,
string connectionName)
=> namingConventions.GetTypeName(connectionName + "Edge", TypeKind.Object);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using HotChocolate.Internal;
using HotChocolate.Resolvers;
using HotChocolate.Tests;
using HotChocolate.Types.Descriptors;

namespace HotChocolate.Types.Pagination;

Expand Down Expand Up @@ -877,6 +878,36 @@ public async Task Explicit_ConnectionName()
schema.MatchSnapshot();
}

[Fact]
public async Task Explicit_ConnectionName_With_NamingConvention()
{
var schema =
await new ServiceCollection()
.AddGraphQL()
.AddConvention<INamingConventions, ConnectionNamingConventions>()
.AddQueryType<ExplicitConnectionName>()
.Services
.BuildServiceProvider()
.GetSchemaAsync();

schema.MatchSnapshot();
}

[Fact]
public async Task Infer_ConnectionName_From_NodeType_With_NamingConvention()
{
var schema =
await new ServiceCollection()
.AddGraphQL()
.AddConvention<INamingConventions, ConnectionNamingConventions>()
.AddQueryType<InferConnectionNameFromNodeType>()
.Services
.BuildServiceProvider()
.GetSchemaAsync();

schema.MatchSnapshot();
}

[Fact]
public async Task SelectProviderByName()
{
Expand Down Expand Up @@ -1249,6 +1280,12 @@ public class InferConnectionNameFromField
public string[] Names() => ["a", "b"];
}

public class InferConnectionNameFromNodeType
{
[UsePaging(InferConnectionNameFromField = false)]
public string[] Names() => ["a", "b"];
}

public class ExplicitConnectionName
{
[UsePaging(ConnectionName = "Connection1")]
Expand Down Expand Up @@ -1339,4 +1376,19 @@ public ImmutableArray<int> Test()
return [];
}
}

private sealed class ConnectionNamingConventions : DefaultNamingConventions
{
public override string GetTypeName(string originalTypeName, TypeKind kind)
{
var name = base.GetTypeName(originalTypeName, kind);

return kind is TypeKind.Object
&&
(name.EndsWith("Connection", StringComparison.Ordinal)
|| name.EndsWith("Edge", StringComparison.Ordinal))
? name + "Named"
: name;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
schema {
query: ExplicitConnectionName
}

"A connection to a list of items."
type Connection1ConnectionNamed {
"Information to aid in pagination."
pageInfo: PageInfo!
"A list of edges."
edges: [Connection1EdgeNamed!]
"A flattened list of the nodes."
nodes: [String!]
}

"An edge in a connection."
type Connection1EdgeNamed {
"A cursor for use in pagination."
cursor: String!
"The item at the end of the edge."
node: String!
}

"A connection to a list of items."
type Connection2ConnectionNamed {
"Information to aid in pagination."
pageInfo: PageInfo!
"A list of edges."
edges: [Connection2EdgeNamed!]
"A flattened list of the nodes."
nodes: [String!]
}

"An edge in a connection."
type Connection2EdgeNamed {
"A cursor for use in pagination."
cursor: String!
"The item at the end of the edge."
node: String!
}

type ExplicitConnectionName {
abc("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): Connection1ConnectionNamed
def("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): Connection2ConnectionNamed
ghi("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): GhiConnectionNamed
}

"A connection to a list of items."
type GhiConnectionNamed {
"Information to aid in pagination."
pageInfo: PageInfo!
"A list of edges."
edges: [GhiEdgeNamed!]
"A flattened list of the nodes."
nodes: [String!]
}

"An edge in a connection."
type GhiEdgeNamed {
"A cursor for use in pagination."
cursor: String!
"The item at the end of the edge."
node: String!
}

"Information about pagination in a connection."
type PageInfo {
"Indicates whether more edges exist following the set defined by the clients arguments."
hasNextPage: Boolean!
"Indicates whether more edges exist prior the set defined by the clients arguments."
hasPreviousPage: Boolean!
"When paginating backwards, the cursor to continue."
startCursor: String
"When paginating forwards, the cursor to continue."
endCursor: String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
schema {
query: InferConnectionNameFromNodeType
}

type InferConnectionNameFromNodeType {
names("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): StringConnectionNamed
}

"Information about pagination in a connection."
type PageInfo {
"Indicates whether more edges exist following the set defined by the clients arguments."
hasNextPage: Boolean!
"Indicates whether more edges exist prior the set defined by the clients arguments."
hasPreviousPage: Boolean!
"When paginating backwards, the cursor to continue."
startCursor: String
"When paginating forwards, the cursor to continue."
endCursor: String
}

"A connection to a list of items."
type StringConnectionNamed {
"Information to aid in pagination."
pageInfo: PageInfo!
"A list of edges."
edges: [StringEdgeNamed!]
"A flattened list of the nodes."
nodes: [String!]
}

"An edge in a connection."
type StringEdgeNamed {
"A cursor for use in pagination."
cursor: String!
"The item at the end of the edge."
node: String!
}
Loading