From e11a67218492054c4f483619c453ab73a593ee08 Mon Sep 17 00:00:00 2001 From: Kevin Carroll Date: Mon, 29 Apr 2024 16:52:25 -0700 Subject: [PATCH 1/6] WIP, split non-null string options into input and output strings --- .../starwars/starwars-api70/Program.cs | 29 --- .../Properties/launchSettings.json | 12 -- .../starwars/starwars-api70/Startup.cs | 174 ------------------ .../starwars/starwars-api70/appsettings.json | 12 -- .../starwars-api70/starwars-api70.csproj | 26 --- src/graphql-aspnet.sln | 7 - .../Formatting/GraphSchemaFormatStrategy.cs | 66 ++++++- .../GraphSchemaFormatStrategyBuilder.cs | 32 +++- .../GraphTypeNullabilityStrategy.cs | 23 ++- .../Directives/Global/DeprecatedDirective.cs | 2 +- .../Interfaces/Schema/IGraphArgument.cs | 11 +- .../Interfaces/Schema/IGraphField.cs | 5 +- .../Interfaces/Schema/IInputGraphField.cs | 13 +- .../TypeTemplates/InputGraphFieldTemplate.cs | 5 +- .../TypeSystem/DefaultValueCloneOptions.cs | 34 ++++ .../Schemas/TypeSystem/GraphFieldArgument.cs | 30 ++- .../Schemas/TypeSystem/InputGraphField.cs | 30 ++- .../Schemas/TypeSystem/MethodGraphField.cs | 5 +- .../TypeSystem/VirtualGraphFieldArgument.cs | 30 ++- src/library-common.props | 2 +- src/library-tests.props | 2 +- .../graphql-aspnet-testframework.csproj | 2 +- .../graphql-aspnet-tests-thirdpartydll.csproj | 2 +- .../WidgetControllerWithDefaultValue.cs | 23 +++ .../WidgetWithDefaultValue.cs | 24 +++ .../GraphSchemaFomatStrategyTests.cs | 136 ++++++++++++-- 26 files changed, 419 insertions(+), 318 deletions(-) delete mode 100644 src/ancillary-projects/starwars/starwars-api70/Program.cs delete mode 100644 src/ancillary-projects/starwars/starwars-api70/Properties/launchSettings.json delete mode 100644 src/ancillary-projects/starwars/starwars-api70/Startup.cs delete mode 100644 src/ancillary-projects/starwars/starwars-api70/appsettings.json delete mode 100644 src/ancillary-projects/starwars/starwars-api70/starwars-api70.csproj create mode 100644 src/graphql-aspnet/Schemas/TypeSystem/DefaultValueCloneOptions.cs create mode 100644 src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetControllerWithDefaultValue.cs create mode 100644 src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetWithDefaultValue.cs diff --git a/src/ancillary-projects/starwars/starwars-api70/Program.cs b/src/ancillary-projects/starwars/starwars-api70/Program.cs deleted file mode 100644 index 85cd05982..000000000 --- a/src/ancillary-projects/starwars/starwars-api70/Program.cs +++ /dev/null @@ -1,29 +0,0 @@ -// ************************************************************* -// project: graphql-aspnet -// -- -// repo: https://github.com/graphql-aspnet -// docs: https://graphql-aspnet.github.io -// -- -// License: MIT -// ************************************************************* - -namespace GraphQL.AspNet.StarWarsAPI7X -{ - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Hosting; - - public static class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); - } -} \ No newline at end of file diff --git a/src/ancillary-projects/starwars/starwars-api70/Properties/launchSettings.json b/src/ancillary-projects/starwars/starwars-api70/Properties/launchSettings.json deleted file mode 100644 index 530c470fd..000000000 --- a/src/ancillary-projects/starwars/starwars-api70/Properties/launchSettings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", - "profiles": { - "StarWars: .NET 7": { - "commandName": "Project", - "applicationUrl": "http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} diff --git a/src/ancillary-projects/starwars/starwars-api70/Startup.cs b/src/ancillary-projects/starwars/starwars-api70/Startup.cs deleted file mode 100644 index 5c6d042db..000000000 --- a/src/ancillary-projects/starwars/starwars-api70/Startup.cs +++ /dev/null @@ -1,174 +0,0 @@ -// ************************************************************* -// project: graphql-aspnet -// -- -// repo: https://github.com/graphql-aspnet -// docs: https://graphql-aspnet.github.io -// -- -// License: MIT -// ************************************************************* - -namespace GraphQL.AspNet.StarWarsAPI7X -{ - using System; - using GraphQL.AspNet; - using GraphQL.AspNet.Configuration; - using GraphQL.AspNet.Execution; - using GraphQL.AspNet.StarwarsAPI.Common.Services; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.WebSockets; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - - public class Startup - { - private const string ALL_ORIGINS_POLICY = "_allOrigins"; - - private static readonly TimeSpan SOCKET_CONNECTION_KEEPALIVE = TimeSpan.FromSeconds(10); - - /// - /// Initializes a new instance of the class. - /// - /// The configuration created to govern the - /// application environment. - public Startup(IConfiguration configuration) - { - this.Configuration = configuration; - } - - /// - /// Configures the service collection to be built for this application instance. - /// - /// The services. - public void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - services.AddScoped(); - - // apply an unrestricted cors policy for the demo services - // to allow use on many of the tools for testing (graphiql, altair etc.) - // Do not do this in production - services.AddCors(options => - { - options.AddPolicy( - ALL_ORIGINS_POLICY, - builder => - { - builder.AllowAnyOrigin() - .AllowAnyHeader() - .AllowAnyMethod(); - }); - }); - - // ASP.NET websockets implementation must also be added to the runtime - services.AddWebSockets((options) => - { - // here add some common origins of various tools that may be - // used for running this demo - // do not add these in a production app unless you need - // to - options.AllowedOrigins.Add("http://localhost:5000"); - options.AllowedOrigins.Add("http://localhost:4000"); - options.AllowedOrigins.Add("http://localhost:3000"); - options.AllowedOrigins.Add("null"); - options.AllowedOrigins.Add("file://"); - options.AllowedOrigins.Add("ws://"); - - }); - - // ---------------------------------------------------------- - // Register GraphQL with the application - // ---------------------------------------------------------- - // By default graphql will scan your assembly for any GraphControllers - // and automatically wire them up to the schema - // you can control which assemblies are scanned and which classes are registered using - // the schema configuration options set here. - // - // in this example because of the two test projects (netcore3.1 and net5.0) - // we have moved all the shared code to a common assembly (starwars-common) and are injecting it - // as a single unit - // - // we then add subscription services to the schema builder returned from .AddGraphQL() - services.AddGraphQL(options => - { - options.ResponseOptions.ExposeExceptions = true; - options.ResponseOptions.MessageSeverityLevel = GraphMessageSeverity.Information; - - // options.ExecutionOptions.EnableMetrics = true; - // options.ResponseOptions.ExposeMetrics = true; - - var assembly = typeof(StarWarsDataRepository).Assembly; - options.AddAssembly(assembly); - }) - .AddSubscriptions(options => - { - // this route path is set by default - // it is listed here just as a matter of example - options.Route = SubscriptionConstants.Routing.DEFAULT_SUBSCRIPTIONS_ROUTE; - - // for some web based graphql tools such as graphiql and graphql-playground - // the default keep-alive timeout of 2 minutes is too long. - // - // still others (like graphql-playground running in electron) do not respond/configure - // for socket-level ping/pong frames to allow for socket-level keep alives - // - // here we set this demo project websocket keep-alive (at the server level) - // to be below all those thresholds to ensure a hassle free experience. - // In practice, you should configure your server (both subscription keep alives and socket keep alives) - // with an interval that is compatiable with your client side environment. - options.ConnectionKeepAliveInterval = SOCKET_CONNECTION_KEEPALIVE; - }); - - // if you have rest controllers this item be sure they are included. - // Graphql and rest can live side by side in the same project without issue - // -------------------------------------------------- - // builder.Services.AddControllers(); - } - - /// - /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - /// - /// The asp.net application builder. - /// The configured host environment. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - app.AddStarWarsStartedMessageToConsole(); - - app.UseRouting(); - - app.UseCors(ALL_ORIGINS_POLICY); - - app.UseAuthorization(); - - // enable web sockets on this server instance - // this must be done before a call to 'UseGraphQL' if subscriptions are enabled for any - // schema otherwise the subscriptions may not register correctly - app.UseWebSockets(); - - // if you have no rest controllers this item can be safely skipped - // graphql and rest can live side by side in the same project without issue - // app.UseEndpoints(endpoints => - // { - // endpoints.MapControllers(); - // }); - - // ************************************************************ - // Finalize the graphql setup: - // 1) Loading the schema - // 2) Publish the route to hook the graphql runtime to ASP.NET. - // - // Be sure to register it after "UseAuthorization" if you need it. - // - // If the construction of your runtime schema has any errors they will be thrown here - // before your application starts listening for requests. - // ************************************************************ - app.UseGraphQL(); - } - - /// - /// Gets the environment configuration for this instance. - /// - /// The configuration item. - public IConfiguration Configuration { get; } - } -} \ No newline at end of file diff --git a/src/ancillary-projects/starwars/starwars-api70/appsettings.json b/src/ancillary-projects/starwars/starwars-api70/appsettings.json deleted file mode 100644 index 289add7ec..000000000 --- a/src/ancillary-projects/starwars/starwars-api70/appsettings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Information", - "Microsoft.AspNetCore.Hosting.Internal.WebHost": "Debug", - "Microsoft.AspNetCore.Hosting.Diagnostics": "Debug", - "GraphQL.AspNet": "Warning" - } - }, - "AllowedHosts": "*" -} diff --git a/src/ancillary-projects/starwars/starwars-api70/starwars-api70.csproj b/src/ancillary-projects/starwars/starwars-api70/starwars-api70.csproj deleted file mode 100644 index a9df881c7..000000000 --- a/src/ancillary-projects/starwars/starwars-api70/starwars-api70.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - net7.0 - latest - $(NoWarn);1701;1702;1705;1591;NU1603 - GraphQL.AspNet.StarWarsAPI7X - GraphQL.AspNet.StarwarsAPI7X - true - - - - ..\..\..\styles.ruleset - - - - - - - - - - - - - diff --git a/src/graphql-aspnet.sln b/src/graphql-aspnet.sln index b796713d9..f3978b89f 100644 --- a/src/graphql-aspnet.sln +++ b/src/graphql-aspnet.sln @@ -31,8 +31,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "graphql-aspnet-subscription EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "graphql-aspnet-subscriptions-tests", "unit-tests\graphql-aspnet-subscriptions-tests\graphql-aspnet-subscriptions-tests.csproj", "{6E4A16F5-1B98-412E-9A88-F56301F5D0E4}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "starwars-api70", "ancillary-projects\starwars\starwars-api70\starwars-api70.csproj", "{B92A5C91-F88D-4F26-8775-20C72692BD43}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "graphql-aspnet-tests-common", "unit-tests\graphql-aspnet-tests-common\graphql-aspnet-tests-common.csproj", "{3CB086E3-5E7B-438B-9A95-AEA264009521}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "starwars-api80", "ancillary-projects\starwars\starwars-api80\starwars-api80.csproj", "{43C9EB6E-5FFE-4EC6-B21B-09C715B7B114}" @@ -70,10 +68,6 @@ Global {6E4A16F5-1B98-412E-9A88-F56301F5D0E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6E4A16F5-1B98-412E-9A88-F56301F5D0E4}.Debug|Any CPU.Build.0 = Debug|Any CPU {6E4A16F5-1B98-412E-9A88-F56301F5D0E4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B92A5C91-F88D-4F26-8775-20C72692BD43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B92A5C91-F88D-4F26-8775-20C72692BD43}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B92A5C91-F88D-4F26-8775-20C72692BD43}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B92A5C91-F88D-4F26-8775-20C72692BD43}.Release|Any CPU.Build.0 = Release|Any CPU {3CB086E3-5E7B-438B-9A95-AEA264009521}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3CB086E3-5E7B-438B-9A95-AEA264009521}.Debug|Any CPU.Build.0 = Debug|Any CPU {3CB086E3-5E7B-438B-9A95-AEA264009521}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -96,7 +90,6 @@ Global {5F6EBAF4-B5EB-4DBD-8F51-17BBC2E8984D} = {22C7BC5B-EC8E-4A07-9968-961E86AB44E2} {5DE081AA-494A-4377-B2CA-6952715D513D} = {350D3594-5D97-4D9B-A01D-D3A5C036318C} {6E4A16F5-1B98-412E-9A88-F56301F5D0E4} = {350D3594-5D97-4D9B-A01D-D3A5C036318C} - {B92A5C91-F88D-4F26-8775-20C72692BD43} = {22C7BC5B-EC8E-4A07-9968-961E86AB44E2} {3CB086E3-5E7B-438B-9A95-AEA264009521} = {350D3594-5D97-4D9B-A01D-D3A5C036318C} {43C9EB6E-5FFE-4EC6-B21B-09C715B7B114} = {22C7BC5B-EC8E-4A07-9968-961E86AB44E2} {E67D4FB2-73FF-4EC1-80F8-5D4DEC57F5AA} = {350D3594-5D97-4D9B-A01D-D3A5C036318C} diff --git a/src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategy.cs b/src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategy.cs index 148b2247c..e9499fbec 100644 --- a/src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategy.cs +++ b/src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategy.cs @@ -9,8 +9,6 @@ namespace GraphQL.AspNet.Configuration.Formatting { - using System; - using System.IO; using GraphQL.AspNet.Common.Extensions; using GraphQL.AspNet.Interfaces.Configuration; using GraphQL.AspNet.Interfaces.Schema; @@ -220,10 +218,13 @@ protected virtual IGraphType FormatGraphType(ISchemaConfiguration configuration, /// IInputGraphField. protected virtual IInputGraphField FormatInputGraphField(ISchemaConfiguration configuration, IInputGraphField inputGraphField) { - var formattedName = this.FormatFieldName(inputGraphField.Name); + if (!inputGraphField.TypeExpression.IsFixed) + { + inputGraphField = this.ApplyTypeExpressionNullabilityStrategy(inputGraphField); + } + var formattedName = this.FormatFieldName(inputGraphField.Name); var typeExpression = inputGraphField.TypeExpression; - typeExpression = typeExpression.Clone(this.FormatGraphTypeName(typeExpression.TypeName)); return inputGraphField.Clone(fieldName: formattedName, typeExpression: typeExpression); @@ -245,6 +246,7 @@ protected virtual IGraphField FormatGraphField(ISchemaConfiguration configuratio var formattedName = this.FormatFieldName(graphField.Name); var typeExpression = graphField.TypeExpression; typeExpression = typeExpression.Clone(this.FormatGraphTypeName(typeExpression.TypeName)); + return graphField.Clone(fieldName: formattedName, typeExpression: typeExpression); } @@ -284,7 +286,7 @@ protected virtual IGraphField ApplyTypeExpressionNullabilityStrategy(IGraphField shouldBeNonNullType = shouldBeNonNullType || (this.NullabilityStrategy - .HasFlag(NullabilityFormatStrategy.NonNullStrings) + .HasFlag(NullabilityFormatStrategy.NonNullOutputStrings) && graphField.ObjectType == typeof(string)); shouldBeNonNullType = shouldBeNonNullType || @@ -304,6 +306,46 @@ protected virtual IGraphField ApplyTypeExpressionNullabilityStrategy(IGraphField return graphField.Clone(typeExpression: newTypeExpression); } + /// + /// For the given field, applies an appropriate nullability strategy + /// according to the rules of this instance and returns a new instance + /// of the field. + /// + /// The graph field to update. + /// IGraphField. + protected virtual IInputGraphField ApplyTypeExpressionNullabilityStrategy(IInputGraphField inputGraphField) + { + GraphTypeExpressionNullabilityStrategies strat = GraphTypeExpressionNullabilityStrategies.None; + + var shouldTypeBeNonNull = this.NullabilityStrategy + .HasFlag(NullabilityFormatStrategy.NonNullInputStrings) + && inputGraphField.ObjectType == typeof(string); + + shouldTypeBeNonNull = shouldTypeBeNonNull || + (this.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullReferenceTypes) + && inputGraphField.ObjectType != typeof(string) + && !inputGraphField.ObjectType.IsValueType); + + if (shouldTypeBeNonNull) + strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullType; + + if (this.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullLists)) + strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullLists; + + var newTypeExpression = inputGraphField.TypeExpression.Clone(strat); + inputGraphField = inputGraphField.Clone(typeExpression: newTypeExpression); + + if (!newTypeExpression.IsNullable && inputGraphField.HasDefaultValue && inputGraphField.DefaultValue is null) + { + // when the field, as a whole, becomes non-nullable and has a default value of null + // the field must become "required" without a default value because of the rules + // of the schema + inputGraphField = inputGraphField.Clone(defaultValueOptions: DefaultValueCloneOptions.MakeRequired); + } + + return inputGraphField; + } + /// /// For the given argument applies an appropriate nullability strategy /// according to the rules of this instance and returns a new instance @@ -316,7 +358,7 @@ protected virtual IGraphArgument ApplyTypeExpressionNullabilityStrategy(IGraphAr GraphTypeExpressionNullabilityStrategies strat = GraphTypeExpressionNullabilityStrategies.None; var shouldBeNonNullType = this.NullabilityStrategy - .HasFlag(NullabilityFormatStrategy.NonNullStrings) + .HasFlag(NullabilityFormatStrategy.NonNullInputStrings) && argument.ObjectType == typeof(string); shouldBeNonNullType = shouldBeNonNullType || @@ -332,7 +374,17 @@ protected virtual IGraphArgument ApplyTypeExpressionNullabilityStrategy(IGraphAr strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullLists; var newTypeExpression = argument.TypeExpression.Clone(strat); - return argument.Clone(typeExpression: newTypeExpression); + argument = argument.Clone(typeExpression: newTypeExpression); + + if (!newTypeExpression.IsNullable && argument.HasDefaultValue && argument.DefaultValue is null) + { + // when the input argument, as a whole, becomes non-nullable and has a default value of null + // the field must become "required" without a default value because of the rules + // of the schema + argument = argument.Clone(defaultValueOptions: DefaultValueCloneOptions.MakeRequired); + } + + return argument; } /// diff --git a/src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategyBuilder.cs b/src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategyBuilder.cs index 32c54493e..4aa430232 100644 --- a/src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategyBuilder.cs +++ b/src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategyBuilder.cs @@ -9,8 +9,6 @@ namespace GraphQL.AspNet.Configuration.Formatting { - using System; - /// /// A builder class to formulate a /// with various options. @@ -44,8 +42,34 @@ public GraphSchemaFormatStrategyBuilder() /// SchemaFormatStrategyBuilder. public GraphSchemaFormatStrategyBuilder WithRequiredStrings() { - if (!_format.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullStrings)) - _format.NullabilityStrategy = _format.NullabilityStrategy | NullabilityFormatStrategy.NonNullStrings; + return this.WithRequiredInputStrings() + .WithRequiredOutputStrings(); + } + + /// + /// Sets a rule such that all string scalar on inbound data items (e.g. field arguments and INPUT_OBJECT types) + /// will be declared as "not null" by default for all generated graph types.This can be overriden on a per field or + /// per argument basis. + /// + /// GraphSchemaFormatStrategyBuilder. + public GraphSchemaFormatStrategyBuilder WithRequiredInputStrings() + { + if (!_format.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullInputStrings)) + _format.NullabilityStrategy = _format.NullabilityStrategy | NullabilityFormatStrategy.NonNullInputStrings; + + return this; + } + + /// + /// Sets a rule such that all string scalar fields on outbound data items (e.g. INTERFACE and OBJECT types) + /// will be declared as "not null" by default for all generated graph types.This can be overriden on a per field or + /// per argument basis. + /// + /// GraphSchemaFormatStrategyBuilder. + public GraphSchemaFormatStrategyBuilder WithRequiredOutputStrings() + { + if (!_format.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullOutputStrings)) + _format.NullabilityStrategy = _format.NullabilityStrategy | NullabilityFormatStrategy.NonNullOutputStrings; return this; } diff --git a/src/graphql-aspnet/Configuration/Formatting/GraphTypeNullabilityStrategy.cs b/src/graphql-aspnet/Configuration/Formatting/GraphTypeNullabilityStrategy.cs index b42946361..4984935fc 100644 --- a/src/graphql-aspnet/Configuration/Formatting/GraphTypeNullabilityStrategy.cs +++ b/src/graphql-aspnet/Configuration/Formatting/GraphTypeNullabilityStrategy.cs @@ -10,7 +10,6 @@ namespace GraphQL.AspNet.Configuration.Formatting { using System; - using System.Data; /// /// A bitwise set of format strategies that can be applied out of the box. @@ -28,7 +27,8 @@ public enum NullabilityFormatStrategy /// /// Intermediate graph types, created when you use path templates on your /// queries and mutations, are marked as non-nullable. This can greatly assist - /// in reducing null-handling noise in various client side code generators. + /// in reducing null-handling noise in various client side code generators. Intermediate graph + /// types will never be null but may be expressed as "nullable" in the schema if this is not set. /// NonNullTemplates = 1, @@ -39,15 +39,28 @@ public enum NullabilityFormatStrategy NonNullLists = 2, /// - /// String scalars are treated like value types and cannot be null by default. + /// String scalars on input items (i.e. field arguments and INPUT_OBJECT fields) are treated like + /// value types and considered "not nullable" by default. /// - NonNullStrings = 4, + NonNullInputStrings = 4, + + /// + /// String scalars on output items (i.e. fields on OBJECT and INTERFACE types) are treated like + /// value types and considered "not nullable" by default. + /// + NonNullOutputStrings = 8, /// /// The schema will treat all class reference types, in any type expression, as being non-nullable by /// default. /// - NonNullReferenceTypes = 8, + NonNullReferenceTypes = 16, + + /// + /// All string scalars, whether as a field argument, input object field, object field or interface field + /// are treated like value types and are considered "not nullable" by default in all instances. + /// + NonNullStrings = NonNullInputStrings | NonNullOutputStrings, /// /// No changes are made to the nullability of different fields. They are used as provided in diff --git a/src/graphql-aspnet/Directives/Global/DeprecatedDirective.cs b/src/graphql-aspnet/Directives/Global/DeprecatedDirective.cs index d032c2261..7e4cded51 100644 --- a/src/graphql-aspnet/Directives/Global/DeprecatedDirective.cs +++ b/src/graphql-aspnet/Directives/Global/DeprecatedDirective.cs @@ -33,7 +33,7 @@ public sealed class DeprecatedDirective : GraphDirective /// IGraphActionResult. [DirectiveLocations(DirectiveLocation.FIELD_DEFINITION | DirectiveLocation.ENUM_VALUE)] public IGraphActionResult Execute( - [FromGraphQL("reason")] + [FromGraphQL("reason", TypeExpression = "Type")] [Description("An optional human-friendly reason explaining why the schema item is being deprecated.")] string reason = "No longer supported") { diff --git a/src/graphql-aspnet/Interfaces/Schema/IGraphArgument.cs b/src/graphql-aspnet/Interfaces/Schema/IGraphArgument.cs index 4e9e51cdb..77f588eae 100644 --- a/src/graphql-aspnet/Interfaces/Schema/IGraphArgument.cs +++ b/src/graphql-aspnet/Interfaces/Schema/IGraphArgument.cs @@ -10,6 +10,7 @@ namespace GraphQL.AspNet.Interfaces.Schema { using GraphQL.AspNet.Schemas; + using GraphQL.AspNet.Schemas.TypeSystem; /// /// An argument/input value that can be applied to a field. @@ -23,8 +24,16 @@ public interface IGraphArgument : ITypedSchemaItem, IDefaultValueSchemaItem, ISc /// When not null, represents the new argument name to use for the cloned instance. /// When not null, represents the new type expression to use /// for this field. + /// A value indicating what to do with field requirements + /// and default values in the cloned field. + /// The new default value if so requested to be applied. /// IGraphField. - IGraphArgument Clone(ISchemaItem parent = null, string argumentName = null, GraphTypeExpression typeExpression = null); + IGraphArgument Clone( + ISchemaItem parent = null, + string argumentName = null, + GraphTypeExpression typeExpression = null, + DefaultValueCloneOptions defaultValueOptions = DefaultValueCloneOptions.None, + object newDefaultValue = null); /// /// Gets the type expression that represents the data of this argument (i.e. the '[SomeType!]' diff --git a/src/graphql-aspnet/Interfaces/Schema/IGraphField.cs b/src/graphql-aspnet/Interfaces/Schema/IGraphField.cs index 644fcfdc7..4dfeb3ed5 100644 --- a/src/graphql-aspnet/Interfaces/Schema/IGraphField.cs +++ b/src/graphql-aspnet/Interfaces/Schema/IGraphField.cs @@ -28,7 +28,10 @@ public interface IGraphField : IDeprecatable, IGraphArgumentContainer, ISecurabl /// When not null, represents the new type expression to use /// for this field. /// IGraphField. - IGraphField Clone(ISchemaItem parent = null, string fieldName = null, GraphTypeExpression typeExpression = null); + IGraphField Clone( + ISchemaItem parent = null, + string fieldName = null, + GraphTypeExpression typeExpression = null); /// /// Updates the field resolver used by this graph field. diff --git a/src/graphql-aspnet/Interfaces/Schema/IInputGraphField.cs b/src/graphql-aspnet/Interfaces/Schema/IInputGraphField.cs index 02cc1cc9e..1a9390870 100644 --- a/src/graphql-aspnet/Interfaces/Schema/IInputGraphField.cs +++ b/src/graphql-aspnet/Interfaces/Schema/IInputGraphField.cs @@ -10,6 +10,7 @@ namespace GraphQL.AspNet.Interfaces.Schema { using GraphQL.AspNet.Schemas; + using GraphQL.AspNet.Schemas.TypeSystem; /// /// A field of data on an INPUT_OBJECT graph type. @@ -23,8 +24,16 @@ public interface IInputGraphField : IGraphFieldBase, IDefaultValueSchemaItem, IT /// When not null, represents the new field name to use for the cloned value. /// When not null, represents the new type expression to use /// for this field. - /// IGraphField. - IInputGraphField Clone(ISchemaItem parent = null, string fieldName = null, GraphTypeExpression typeExpression = null); + /// A value indicating what to do with field requirements + /// and default values in the cloned field. + /// The new default value if so requested to be applied. + /// IInputGraphField. + IInputGraphField Clone( + ISchemaItem parent = null, + string fieldName = null, + GraphTypeExpression typeExpression = null, + DefaultValueCloneOptions defaultValueOptions = DefaultValueCloneOptions.None, + object newDefaultValue = null); /// /// Gets the unaltered name of the property that defines this input field in source code. diff --git a/src/graphql-aspnet/Schemas/Generation/TypeTemplates/InputGraphFieldTemplate.cs b/src/graphql-aspnet/Schemas/Generation/TypeTemplates/InputGraphFieldTemplate.cs index f7cf99846..5832c7801 100644 --- a/src/graphql-aspnet/Schemas/Generation/TypeTemplates/InputGraphFieldTemplate.cs +++ b/src/graphql-aspnet/Schemas/Generation/TypeTemplates/InputGraphFieldTemplate.cs @@ -63,6 +63,9 @@ protected override void ParseTemplateDefinition() var typeExpression = GraphTypeExpression.FromType(this.DeclaredReturnType, this.DeclaredTypeWrappers); typeExpression = typeExpression.Clone(Constants.Other.DEFAULT_TYPE_EXPRESSION_TYPE_NAME); + if (this.IsCustomTypeExpression) + typeExpression = typeExpression.ToFixed(); + this.IsRequired = this.AttributeProvider.SingleAttributeOrDefault() != null; this.TypeExpression = typeExpression; @@ -146,7 +149,7 @@ public override void ValidateOrThrow(bool validateChildren = true) public GraphTypeExpression TypeExpression { get; private set; } /// - public bool IsCustomTypeExpression => this.DeclaredTypeWrappers != null; + public bool IsCustomTypeExpression => _fieldDeclaration?.TypeExpression != null; /// public TypeKind OwnerTypeKind => TypeKind.INPUT_OBJECT; diff --git a/src/graphql-aspnet/Schemas/TypeSystem/DefaultValueCloneOptions.cs b/src/graphql-aspnet/Schemas/TypeSystem/DefaultValueCloneOptions.cs new file mode 100644 index 000000000..952e1dc17 --- /dev/null +++ b/src/graphql-aspnet/Schemas/TypeSystem/DefaultValueCloneOptions.cs @@ -0,0 +1,34 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Schemas.TypeSystem +{ + /// + /// A set of options to instruct how a field clone operation should handle a default value + /// applied to a field argument or input object field. + /// + public enum DefaultValueCloneOptions + { + /// + /// No change to the requirability of the item is made. + /// + None = 0, + + /// + /// The item is marked as required and any assigned default value is removed. + /// + MakeRequired = 1, + + /// + /// The item is marked as not required and the supplied default value is set as the + /// value for the field. + /// + UpdateDefaultValue = 2, + } +} diff --git a/src/graphql-aspnet/Schemas/TypeSystem/GraphFieldArgument.cs b/src/graphql-aspnet/Schemas/TypeSystem/GraphFieldArgument.cs index 2e04ac50b..feeb141ce 100644 --- a/src/graphql-aspnet/Schemas/TypeSystem/GraphFieldArgument.cs +++ b/src/graphql-aspnet/Schemas/TypeSystem/GraphFieldArgument.cs @@ -73,7 +73,12 @@ public GraphFieldArgument( } /// - public IGraphArgument Clone(ISchemaItem parent = null, string argumentName = null, GraphTypeExpression typeExpression = null) + public IGraphArgument Clone( + ISchemaItem parent = null, + string argumentName = null, + GraphTypeExpression typeExpression = null, + DefaultValueCloneOptions defaultValueOptions = DefaultValueCloneOptions.None, + object newDefaultValue = null) { parent = parent ?? this.Parent; @@ -82,6 +87,25 @@ public IGraphArgument Clone(ISchemaItem parent = null, string argumentName = nul var parentPath = parent?.ItemPath ?? this.ItemPath.Parent; var itemPath = parentPath.CreateChild(argumentName); + var hasDefaultValue = this.HasDefaultValue; + var defaultValue = this.DefaultValue; + + switch (defaultValueOptions) + { + case DefaultValueCloneOptions.None: + break; + + case DefaultValueCloneOptions.MakeRequired: + defaultValue = null; + hasDefaultValue = false; + break; + + case DefaultValueCloneOptions.UpdateDefaultValue: + defaultValue = newDefaultValue; + hasDefaultValue = true; + break; + } + var clonedItem = new GraphFieldArgument( parent, argumentName, @@ -90,8 +114,8 @@ public IGraphArgument Clone(ISchemaItem parent = null, string argumentName = nul typeExpression ?? this.TypeExpression.Clone(), itemPath, this.ObjectType, - this.HasDefaultValue, - this.DefaultValue, + hasDefaultValue, + defaultValue, this.Description, this.AppliedDirectives); diff --git a/src/graphql-aspnet/Schemas/TypeSystem/InputGraphField.cs b/src/graphql-aspnet/Schemas/TypeSystem/InputGraphField.cs index 7a9fc2305..18c7e9bfb 100644 --- a/src/graphql-aspnet/Schemas/TypeSystem/InputGraphField.cs +++ b/src/graphql-aspnet/Schemas/TypeSystem/InputGraphField.cs @@ -22,6 +22,8 @@ namespace GraphQL.AspNet.Schemas.TypeSystem [DebuggerDisplay("Field: {ItemPath.Path}")] public class InputGraphField : IInputGraphField { + private readonly bool _declaredIsRequired; + // ******************************************* // implementation note: // @@ -83,13 +85,33 @@ public InputGraphField( public virtual IInputGraphField Clone( ISchemaItem parent = null, string fieldName = null, - GraphTypeExpression typeExpression = null) + GraphTypeExpression typeExpression = null, + DefaultValueCloneOptions defaultValueOptions = DefaultValueCloneOptions.None, + object newDefaultValue = null) { parent = parent ?? this.Parent; var itemPath = parent?.ItemPath.CreateChild(this.ItemPath.Name) ?? this.ItemPath.Clone(); fieldName = fieldName?.Trim() ?? this.Name; + var declareIsRequired = _declaredIsRequired; + var defaultValue = this.DefaultValue; + + switch (defaultValueOptions) + { + case DefaultValueCloneOptions.None: + break; + + case DefaultValueCloneOptions.MakeRequired: + declareIsRequired = true; + defaultValue = null; + break; + + case DefaultValueCloneOptions.UpdateDefaultValue: + defaultValue = newDefaultValue; + break; + } + var clonedItem = new InputGraphField( fieldName, this.InternalName, @@ -98,8 +120,8 @@ public virtual IInputGraphField Clone( this.ObjectType, this.DeclaredName, this.DeclaredReturnType, - _declaredIsRequired, - this.DefaultValue, + declareIsRequired, + defaultValue, this.AppliedDirectives); clonedItem.Parent = parent; @@ -127,8 +149,6 @@ public virtual IInputGraphField Clone( /// public IAppliedDirectiveCollection AppliedDirectives { get; } - private readonly bool _declaredIsRequired; - /// public string Name { get; } diff --git a/src/graphql-aspnet/Schemas/TypeSystem/MethodGraphField.cs b/src/graphql-aspnet/Schemas/TypeSystem/MethodGraphField.cs index 62ab4b5c0..9ce775418 100644 --- a/src/graphql-aspnet/Schemas/TypeSystem/MethodGraphField.cs +++ b/src/graphql-aspnet/Schemas/TypeSystem/MethodGraphField.cs @@ -77,7 +77,10 @@ public void UpdateResolver(IGraphFieldResolver newResolver, FieldResolutionMode? } /// - public virtual IGraphField Clone(ISchemaItem parent = null, string fieldName = null, GraphTypeExpression typeExpression = null) + public virtual IGraphField Clone( + ISchemaItem parent = null, + string fieldName = null, + GraphTypeExpression typeExpression = null) { parent = parent ?? this.Parent; fieldName = fieldName?.Trim() ?? this.Name; diff --git a/src/graphql-aspnet/Schemas/TypeSystem/VirtualGraphFieldArgument.cs b/src/graphql-aspnet/Schemas/TypeSystem/VirtualGraphFieldArgument.cs index 18f752b45..b7b00a6fb 100644 --- a/src/graphql-aspnet/Schemas/TypeSystem/VirtualGraphFieldArgument.cs +++ b/src/graphql-aspnet/Schemas/TypeSystem/VirtualGraphFieldArgument.cs @@ -61,7 +61,12 @@ public VirtualGraphFieldArgument( } /// - public IGraphArgument Clone(ISchemaItem parent = null, string argumentName = null, GraphTypeExpression typeExpression = null) + public IGraphArgument Clone( + ISchemaItem parent = null, + string argumentName = null, + GraphTypeExpression typeExpression = null, + DefaultValueCloneOptions defaultValueOptions = DefaultValueCloneOptions.None, + object newDefaultValue = null) { parent = parent ?? this.Parent; @@ -70,6 +75,25 @@ public IGraphArgument Clone(ISchemaItem parent = null, string argumentName = nul var parentPath = parent?.ItemPath ?? this.ItemPath.Parent; var itemPath = parentPath.CreateChild(argumentName); + var hasDefaultValue = this.HasDefaultValue; + var defaultValue = this.DefaultValue; + + switch (defaultValueOptions) + { + case DefaultValueCloneOptions.None: + break; + + case DefaultValueCloneOptions.MakeRequired: + defaultValue = null; + hasDefaultValue = false; + break; + + case DefaultValueCloneOptions.UpdateDefaultValue: + defaultValue = newDefaultValue; + hasDefaultValue = true; + break; + } + var clonedItem = new VirtualGraphFieldArgument( parent, argumentName, @@ -77,8 +101,8 @@ public IGraphArgument Clone(ISchemaItem parent = null, string argumentName = nul typeExpression ?? this.TypeExpression.Clone(), itemPath, this.ObjectType, - this.HasDefaultValue, - this.DefaultValue); + hasDefaultValue, + defaultValue); clonedItem.Description = this.Description; diff --git a/src/library-common.props b/src/library-common.props index 72f5d0430..68621ac96 100644 --- a/src/library-common.props +++ b/src/library-common.props @@ -1,7 +1,7 @@ - net8.0;net7.0;net6.0; + net8.0;net6.0; latest $(NoWarn);1701;1702;1705;1591;NU1603;IDE0019;IDE0017;RCS1146;RCS1194; diff --git a/src/library-tests.props b/src/library-tests.props index 37541a616..5d3fee9ea 100644 --- a/src/library-tests.props +++ b/src/library-tests.props @@ -1,7 +1,7 @@ - net8.0;net7.0;net6.0; + net8.0;net6.0; latest $(NoWarn);1701;1702;1705;1591;NU1603;IDE0019;IDE0017;RCS1146;RCS1194; GraphQL.AspNet.Tests diff --git a/src/unit-tests/graphql-aspnet-testframework/graphql-aspnet-testframework.csproj b/src/unit-tests/graphql-aspnet-testframework/graphql-aspnet-testframework.csproj index d294329bb..ee9dc7f42 100644 --- a/src/unit-tests/graphql-aspnet-testframework/graphql-aspnet-testframework.csproj +++ b/src/unit-tests/graphql-aspnet-testframework/graphql-aspnet-testframework.csproj @@ -2,7 +2,7 @@ - net8.0;net7.0;net6.0; + net8.0;net6.0; GraphQL.AspNet.Tests.Framework GraphQL.AspNet.TestFramework GraphQL ASP.NET Test Framework diff --git a/src/unit-tests/graphql-aspnet-tests-thirdpartydll/graphql-aspnet-tests-thirdpartydll.csproj b/src/unit-tests/graphql-aspnet-tests-thirdpartydll/graphql-aspnet-tests-thirdpartydll.csproj index fa889e4e6..246a3ca57 100644 --- a/src/unit-tests/graphql-aspnet-tests-thirdpartydll/graphql-aspnet-tests-thirdpartydll.csproj +++ b/src/unit-tests/graphql-aspnet-tests-thirdpartydll/graphql-aspnet-tests-thirdpartydll.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0; + net8.0;net6.0; latest $(NoWarn);1701;1702;1705;1591;NU1603;IDE0060;IDE0052;IDE0044;IDE0059;IDE0052 GraphQL.AspNet.Tests.ThirdPartyDll diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetControllerWithDefaultValue.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetControllerWithDefaultValue.cs new file mode 100644 index 000000000..1cff269a4 --- /dev/null +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetControllerWithDefaultValue.cs @@ -0,0 +1,23 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Tests.Configuration.FormatStrategyTestData +{ + using GraphQL.AspNet.Attributes; + using GraphQL.AspNet.Controllers; + + public class WidgetControllerWithDefaultValue : GraphController + { + [QueryRoot("retrieveRootWidget")] + public Widget RetrieveWidgetFromRoot(string arg1 = "default 1") + { + return new Widget(); + } + } +} \ No newline at end of file diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetWithDefaultValue.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetWithDefaultValue.cs new file mode 100644 index 000000000..b0286cfce --- /dev/null +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetWithDefaultValue.cs @@ -0,0 +1,24 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Tests.Configuration.FormatStrategyTestData +{ + public class WidgetWithDefaultValue + { + public WidgetWithDefaultValue() + { + this.StringProp = "default 1"; + this.IntProp = 4; + } + + public string StringProp { get; set; } + + public int IntProp { get; set; } + } +} diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFomatStrategyTests.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFomatStrategyTests.cs index 2d33798bb..e59898634 100644 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFomatStrategyTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFomatStrategyTests.cs @@ -10,10 +10,9 @@ namespace GraphQL.AspNet.Tests.Configuration { using System; - using System.Threading.Tasks; using GraphQL.AspNet.Configuration.Formatting; using GraphQL.AspNet.Interfaces.Schema; - using GraphQL.AspNet.Tests.Common.CommonHelpers; + using GraphQL.AspNet.Schemas.TypeSystem; using GraphQL.AspNet.Tests.Configuration.FormatStrategyTestData; using GraphQL.AspNet.Tests.Framework; using NUnit.Framework; @@ -46,6 +45,8 @@ public class GraphSchemaFomatStrategyTests // string type, not fixed, object graph type [TestCase(NFS.None, typeof(Widget), "stringProp", "String")] [TestCase(NFS.NonNullStrings, typeof(Widget), "stringProp", "String!")] + [TestCase(NFS.NonNullInputStrings, typeof(Widget), "stringProp", "String")] + [TestCase(NFS.NonNullOutputStrings, typeof(Widget), "stringProp", "String!")] [TestCase(NFS.NonNullLists, typeof(Widget), "stringProp", "String")] [TestCase(NFS.NonNullTemplates, typeof(Widget), "stringProp", "String")] [TestCase(NFS.NonNullReferenceTypes, typeof(Widget), "stringProp", "String")] @@ -53,6 +54,8 @@ public class GraphSchemaFomatStrategyTests // string, fixed, object graph type [TestCase(NFS.None, typeof(Widget), "fixedStringProp", "String")] [TestCase(NFS.NonNullStrings, typeof(Widget), "fixedStringProp", "String")] + [TestCase(NFS.NonNullInputStrings, typeof(Widget), "fixedStringProp", "String")] + [TestCase(NFS.NonNullOutputStrings, typeof(Widget), "fixedStringProp", "String")] [TestCase(NFS.NonNullLists, typeof(Widget), "fixedStringProp", "String")] [TestCase(NFS.NonNullTemplates, typeof(Widget), "fixedStringProp", "String")] [TestCase(NFS.NonNullReferenceTypes, typeof(Widget), "fixedStringProp", "String")] @@ -95,6 +98,8 @@ public class GraphSchemaFomatStrategyTests // string type, not fixed, interface graph type [TestCase(NFS.None, typeof(IWidgetInterface), "stringProp", "String")] [TestCase(NFS.NonNullStrings, typeof(IWidgetInterface), "stringProp", "String!")] + [TestCase(NFS.NonNullInputStrings, typeof(IWidgetInterface), "stringProp", "String")] + [TestCase(NFS.NonNullOutputStrings, typeof(IWidgetInterface), "stringProp", "String!")] [TestCase(NFS.NonNullLists, typeof(IWidgetInterface), "stringProp", "String")] [TestCase(NFS.NonNullTemplates, typeof(IWidgetInterface), "stringProp", "String")] [TestCase(NFS.NonNullReferenceTypes, typeof(IWidgetInterface), "stringProp", "String")] @@ -102,6 +107,8 @@ public class GraphSchemaFomatStrategyTests // string, fixed, interface graph type [TestCase(NFS.None, typeof(IWidgetInterface), "fixedStringProp", "String")] [TestCase(NFS.NonNullStrings, typeof(IWidgetInterface), "fixedStringProp", "String")] + [TestCase(NFS.NonNullInputStrings, typeof(IWidgetInterface), "fixedStringProp", "String")] + [TestCase(NFS.NonNullOutputStrings, typeof(IWidgetInterface), "fixedStringProp", "String")] [TestCase(NFS.NonNullLists, typeof(IWidgetInterface), "fixedStringProp", "String")] [TestCase(NFS.NonNullTemplates, typeof(IWidgetInterface), "fixedStringProp", "String")] [TestCase(NFS.NonNullReferenceTypes, typeof(IWidgetInterface), "fixedStringProp", "String")] @@ -146,6 +153,8 @@ public void Integration_ConcreteObjectTypes_FieldTypeExpressionTests( [TestCase(NFS.None, "Query_Widgets", "path1", "Query_Widgets_Path1")] [TestCase(NFS.NonNullTemplates, "Query_Widgets", "path1", "Query_Widgets_Path1!")] [TestCase(NFS.NonNullStrings, "Query_Widgets", "path1", "Query_Widgets_Path1")] + [TestCase(NFS.NonNullInputStrings, "Query_Widgets", "path1", "Query_Widgets_Path1")] + [TestCase(NFS.NonNullOutputStrings, "Query_Widgets", "path1", "Query_Widgets_Path1")] [TestCase(NFS.NonNullLists, "Query_Widgets", "path1", "Query_Widgets_Path1")] [TestCase(NFS.NonNullReferenceTypes, "Query_Widgets", "path1", "Query_Widgets_Path1")] @@ -153,9 +162,10 @@ public void Integration_ConcreteObjectTypes_FieldTypeExpressionTests( [TestCase(NFS.None, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] [TestCase(NFS.NonNullTemplates, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2!")] [TestCase(NFS.NonNullStrings, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] + [TestCase(NFS.NonNullInputStrings, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] [TestCase(NFS.NonNullLists, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] [TestCase(NFS.NonNullReferenceTypes, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] - public void Integration_Controllers_FieldTypeExpressionNullabilityTests( + public void VirtualFields_FieldTypeExpressionNullabilityTests( NFS strategy, string graphTypeName, string fieldName, @@ -181,21 +191,29 @@ public void Integration_Controllers_FieldTypeExpressionNullabilityTests( [TestCase(NFS.None, "intArgument", "Int!")] [TestCase(NFS.NonNullTemplates, "intArgument", "Int!")] [TestCase(NFS.NonNullStrings, "intArgument", "Int!")] + [TestCase(NFS.NonNullInputStrings, "intArgument", "Int!")] + [TestCase(NFS.NonNullOutputStrings, "intArgument", "Int!")] [TestCase(NFS.NonNullReferenceTypes, "intArgument", "Int!")] [TestCase(NFS.None, "intArgumentFixed", "Int!")] [TestCase(NFS.NonNullTemplates, "intArgumentFixed", "Int!")] [TestCase(NFS.NonNullStrings, "intArgumentFixed", "Int!")] + [TestCase(NFS.NonNullInputStrings, "intArgumentFixed", "Int!")] + [TestCase(NFS.NonNullOutputStrings, "intArgumentFixed", "Int!")] [TestCase(NFS.NonNullReferenceTypes, "intArgumentFixed", "Int!")] [TestCase(NFS.None, "stringArgument", "String")] [TestCase(NFS.NonNullTemplates, "stringArgument", "String")] [TestCase(NFS.NonNullStrings, "stringArgument", "String!")] + [TestCase(NFS.NonNullInputStrings, "stringArgument", "String!")] + [TestCase(NFS.NonNullOutputStrings, "stringArgument", "String")] [TestCase(NFS.NonNullReferenceTypes, "stringArgument", "String")] [TestCase(NFS.None, "stringArgumentFixed", "String")] [TestCase(NFS.NonNullTemplates, "stringArgumentFixed", "String")] [TestCase(NFS.NonNullStrings, "stringArgumentFixed", "String")] + [TestCase(NFS.NonNullInputStrings, "stringArgumentFixed", "String")] + [TestCase(NFS.NonNullOutputStrings, "stringArgumentFixed", "String")] [TestCase(NFS.NonNullReferenceTypes, "stringArgumentFixed", "String")] [TestCase(NFS.None, "inputObjectArgument", "Input_Widget")] @@ -207,7 +225,7 @@ public void Integration_Controllers_FieldTypeExpressionNullabilityTests( [TestCase(NFS.NonNullTemplates, "inputObjectArgumentFixed", "Input_Widget")] [TestCase(NFS.NonNullStrings, "inputObjectArgumentFixed", "Input_Widget")] [TestCase(NFS.NonNullReferenceTypes, "inputObjectArgumentFixed", "Input_Widget")] - public void Integration_ArgumentTypeExpressionNullabilityTests( + public void Integration_FieldArgumentTypeExpressionNullabilityTests( NFS strategy, string fieldName, string expectedTypeExpression) @@ -230,19 +248,40 @@ public void Integration_ArgumentTypeExpressionNullabilityTests( Assert.AreEqual(expectedTypeExpression, arg1.TypeExpression.ToString()); } - [TestCase(NFS.None, "tripleListProp", "[[[String]]]")] - [TestCase(NFS.NonNullStrings, "tripleListProp", "[[[String!]]]")] - [TestCase(NFS.NonNullReferenceTypes, "tripleListProp", "[[[String]]]")] - [TestCase(NFS.NonNullLists, "tripleListProp", "[[[String]!]!]!")] - [TestCase(NFS.NonNullLists | NFS.NonNullStrings, "tripleListProp", "[[[String!]!]!]!")] - - [TestCase(NFS.None, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NFS.NonNullStrings, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NFS.NonNullLists, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NFS.NonNullReferenceTypes, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NFS.NonNullLists | NFS.NonNullStrings, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NFS.None, TypeKind.OBJECT, "tripleListProp", "[[[String]]]")] + [TestCase(NFS.NonNullStrings, TypeKind.OBJECT, "tripleListProp", "[[[String!]]]")] + [TestCase(NFS.NonNullInputStrings, TypeKind.OBJECT, "tripleListProp", "[[[String]]]")] + [TestCase(NFS.NonNullOutputStrings, TypeKind.OBJECT, "tripleListProp", "[[[String!]]]")] + [TestCase(NFS.NonNullReferenceTypes, TypeKind.OBJECT, "tripleListProp", "[[[String]]]")] + [TestCase(NFS.NonNullLists, TypeKind.OBJECT, "tripleListProp", "[[[String]!]!]!")] + [TestCase(NFS.NonNullLists | NFS.NonNullStrings, TypeKind.OBJECT, "tripleListProp", "[[[String!]!]!]!")] + + [TestCase(NFS.None, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NFS.NonNullStrings, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NFS.NonNullInputStrings, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NFS.NonNullOutputStrings, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NFS.NonNullLists, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NFS.NonNullReferenceTypes, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NFS.NonNullLists | NFS.NonNullStrings, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] + + [TestCase(NFS.None, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]]]")] + [TestCase(NFS.NonNullStrings, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String!]]]")] + [TestCase(NFS.NonNullInputStrings, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String!]]]")] + [TestCase(NFS.NonNullOutputStrings, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]]]")] + [TestCase(NFS.NonNullReferenceTypes, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]]]")] + [TestCase(NFS.NonNullLists, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]!]!]!")] + [TestCase(NFS.NonNullLists | NFS.NonNullStrings, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String!]!]!]!")] + + [TestCase(NFS.None, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NFS.NonNullStrings, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NFS.NonNullInputStrings, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NFS.NonNullOutputStrings, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NFS.NonNullLists, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NFS.NonNullReferenceTypes, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NFS.NonNullLists | NFS.NonNullStrings, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] public void Integration_Objects_FieldTypeExpressionListNullabilityTests( NFS strategy, + TypeKind typeKind, string fieldName, string expectedTypeExpression) { @@ -252,15 +291,23 @@ public void Integration_Objects_FieldTypeExpressionListNullabilityTests( var server = new TestServerBuilder() .AddGraphQL(o => { - o.AddType(); + o.AddType(typeKind); o.DeclarationOptions.SchemaFormatStrategy = formatter; }) .Build(); - var graphType = server.Schema.KnownTypes.FindGraphType(typeof(WidgetList)) as IGraphFieldContainer; - var field = graphType.Fields[fieldName]; - - Assert.AreEqual(expectedTypeExpression, field.TypeExpression.ToString()); + if (typeKind == TypeKind.OBJECT) + { + var graphType = server.Schema.KnownTypes.FindGraphType(typeof(WidgetList)) as IGraphFieldContainer; + var field = graphType.Fields[fieldName]; + Assert.AreEqual(expectedTypeExpression, field.TypeExpression.ToString()); + } + else if (typeKind == TypeKind.INPUT_OBJECT) + { + var graphType = server.Schema.KnownTypes.FindGraphType(typeof(WidgetList)) as IInputObjectGraphType; + var field = graphType.Fields[fieldName]; + Assert.AreEqual(expectedTypeExpression, field.TypeExpression.ToString()); + } } [TestCase(NFS.None, "intArgument", "[Int!]")] @@ -275,11 +322,15 @@ public void Integration_Objects_FieldTypeExpressionListNullabilityTests( [TestCase(NFS.None, "stringArgument", "[String]")] [TestCase(NFS.NonNullStrings, "stringArgument", "[String!]")] + [TestCase(NFS.NonNullInputStrings, "stringArgument", "[String!]")] + [TestCase(NFS.NonNullOutputStrings, "stringArgument", "[String]")] [TestCase(NFS.NonNullLists, "stringArgument", "[String]!")] [TestCase(NFS.NonNullLists | NFS.NonNullStrings, "stringArgument", "[String!]!")] [TestCase(NFS.None, "stringArgumentFixed", "[String]")] [TestCase(NFS.NonNullStrings, "stringArgumentFixed", "[String]")] + [TestCase(NFS.NonNullInputStrings, "stringArgumentFixed", "[String]")] + [TestCase(NFS.NonNullOutputStrings, "stringArgumentFixed", "[String]")] [TestCase(NFS.NonNullLists, "stringArgumentFixed", "[String]")] [TestCase(NFS.NonNullLists | NFS.NonNullStrings, "stringArgumentFixed", "[String]")] @@ -318,5 +369,50 @@ public void Integration_Controllers_FieldTypeExpressionListNullabilityTests( Assert.AreEqual(expectedTypeExpression, arg1.TypeExpression.ToString()); } + + [Test] + public void InputObject_NullableField_WithNonNullDefault_DefaultIsRetained_WhenFieldMadeNonNull() + { + var formatter = new GraphSchemaFormatStrategy(); + formatter.NullabilityStrategy = NFS.NonNullInputStrings; + + var server = new TestServerBuilder() + .AddGraphQL(o => + { + o.AddType(TypeKind.INPUT_OBJECT); + o.DeclarationOptions.SchemaFormatStrategy = formatter; + }) + .Build(); + + var graphType = server.Schema.KnownTypes.FindGraphType($"Input_{nameof(WidgetWithDefaultValue)}") as IInputObjectGraphType; + var field = graphType.Fields["stringProp"]; + + Assert.AreEqual("String!", field.TypeExpression.ToString()); + Assert.IsTrue(field.HasDefaultValue); + Assert.AreEqual("default 1", field.DefaultValue.ToString()); + } + + [Test] + public void FieldArgument_WithNonNullDefault_DefaultIsRetained_WhenFieldMadeNonNull() + { + var formatter = new GraphSchemaFormatStrategy(); + formatter.NullabilityStrategy = NFS.NonNullInputStrings; + + var server = new TestServerBuilder() + .AddGraphQL(o => + { + o.AddController(); + o.DeclarationOptions.SchemaFormatStrategy = formatter; + }) + .Build(); + + var graphType = server.Schema.KnownTypes.FindGraphType("Query") as IGraphFieldContainer; + var field = graphType.Fields["retrieveRootWidget"]; + var arg1 = field.Arguments["arg1"]; + + Assert.AreEqual("String!", arg1.TypeExpression.ToString()); + Assert.IsTrue(arg1.HasDefaultValue); + Assert.AreEqual("default 1", arg1.DefaultValue.ToString()); + } } } \ No newline at end of file From 6c56a54c5c88dedd4ee7d4de5a2d8f7d3e640b92 Mon Sep 17 00:00:00 2001 From: Kevin Carroll Date: Fri, 17 May 2024 14:35:59 -0700 Subject: [PATCH 2/6] renamed schema format strategy condensed name methods to 1 with an specifier parameter and extracted an interface for further customization --- .../Formatting/NameFormatTarget.cs | 40 ++ ...matStrategy.cs => SchemaFormatStrategy.cs} | 195 ++++---- ...lder.cs => SchemaFormatStrategyBuilder.cs} | 56 +-- ...tegy.cs => SchemaItemNameFormatOptions.cs} | 2 +- ...> TypeExpressionNullabilityFormatRules.cs} | 8 +- .../SchemaDeclarationConfiguration.cs | 3 +- ...DefaultGraphQLSchemaFactory_Controllers.cs | 11 +- .../Execution/Response/ResponseWriterBase.cs | 9 +- .../ISchemaDeclarationConfiguration.cs | 5 +- .../Configuration/ISchemaFormatStrategy.cs | 59 +++ .../AuthorizeQueryOperationMiddleware.cs | 1 + .../Generation/TypeMakers/DirectiveMaker.cs | 2 +- .../TypeMakers/EnumGraphTypeMaker.cs | 4 +- .../TypeMakers/GraphArgumentMaker.cs | 2 +- .../Generation/TypeMakers/GraphFieldMaker.cs | 4 +- .../TypeMakers/InputObjectGraphTypeMaker.cs | 2 +- .../TypeMakers/InterfaceGraphTypeMaker.cs | 2 +- .../TypeMakers/ObjectGraphTypeMaker.cs | 2 +- .../TypeMakers/ScalarGraphTypeMaker.cs | 2 +- .../TypeMakers/UnionGraphTypeMaker.cs | 2 +- .../Schemas/GraphTypeExpression.cs | 5 +- .../GraphQLSchemaHelperMethods.cs | 2 +- .../TestServerBuilder{TSchema}.cs | 2 +- .../ConfigurationFieldNamingTests.cs | 6 +- .../GraphSchemaFomatStrategyTests.cs | 426 +++++++++--------- .../GraphSchemaFormatStrategyBuilderTests.cs | 62 +-- .../TypeMakers/EnumGraphTypeMakerTests.cs | 4 +- .../TypeMakers/ScalarGraphTypeMakerTests.cs | 46 +- 28 files changed, 538 insertions(+), 426 deletions(-) create mode 100644 src/graphql-aspnet/Configuration/Formatting/NameFormatTarget.cs rename src/graphql-aspnet/Configuration/Formatting/{GraphSchemaFormatStrategy.cs => SchemaFormatStrategy.cs} (71%) rename src/graphql-aspnet/Configuration/Formatting/{GraphSchemaFormatStrategyBuilder.cs => SchemaFormatStrategyBuilder.cs} (70%) rename src/graphql-aspnet/Configuration/Formatting/{GraphNameFormatStrategy.cs => SchemaItemNameFormatOptions.cs} (93%) rename src/graphql-aspnet/Configuration/Formatting/{GraphTypeNullabilityStrategy.cs => TypeExpressionNullabilityFormatRules.cs} (92%) create mode 100644 src/graphql-aspnet/Interfaces/Configuration/ISchemaFormatStrategy.cs diff --git a/src/graphql-aspnet/Configuration/Formatting/NameFormatTarget.cs b/src/graphql-aspnet/Configuration/Formatting/NameFormatTarget.cs new file mode 100644 index 000000000..c636574c6 --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/NameFormatTarget.cs @@ -0,0 +1,40 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting +{ + /// + /// The different categories of entities that support + /// distinct name formatting within a schema. + /// + public enum NameFormatCategory + { + /// + /// The string is being formatted as the name of a field, be it a field on an INPUT_OBJECT, INTERFACE + /// or OBJECT type. + /// + Field, + + /// + /// The string is being formatted as the name of a formal graph type in the schema. + /// + GraphType, + + /// + /// The string is being formatted as a single enum value label within an ENUM graph type. + /// + EnumValue, + + /// + /// The string is being formatted as the name of a directive used in a query without the preceeding '@' + /// symbol. + /// + Directive, + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategy.cs b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs similarity index 71% rename from src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategy.cs rename to src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs index e9499fbec..e80076ea2 100644 --- a/src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategy.cs +++ b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs @@ -9,6 +9,7 @@ namespace GraphQL.AspNet.Configuration.Formatting { + using System; using GraphQL.AspNet.Common.Extensions; using GraphQL.AspNet.Interfaces.Configuration; using GraphQL.AspNet.Interfaces.Schema; @@ -19,34 +20,34 @@ namespace GraphQL.AspNet.Configuration.Formatting /// A strategy, employed by a schema, to make programatic alterations /// to schema items as they are being constructed. /// - public class GraphSchemaFormatStrategy + public class SchemaFormatStrategy : ISchemaFormatStrategy { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// A single format strategy to use for all naming formats. - public GraphSchemaFormatStrategy(GraphNameFormatStrategy singleStrategy) + /// A single format strategy to use for all naming formats. + public SchemaFormatStrategy(SchemaItemNameFormatOptions nameFormatStrategy) : this( - NullabilityFormatStrategy.Default, - singleStrategy, - singleStrategy, - singleStrategy) + TypeExpressionNullabilityFormatRules.Default, + nameFormatStrategy, + nameFormatStrategy, + nameFormatStrategy) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The strategy used to augment nullability /// checks on type expressions for fields and arguments. /// The format strategy to use for graph type names. /// The format strategy to use for field names. /// The format strategy to use for enum values. - public GraphSchemaFormatStrategy( - NullabilityFormatStrategy nullabilityStrategy = NullabilityFormatStrategy.Default, - GraphNameFormatStrategy typeNameStrategy = GraphNameFormatStrategy.ProperCase, - GraphNameFormatStrategy fieldNameStrategy = GraphNameFormatStrategy.CamelCase, - GraphNameFormatStrategy enumValueStrategy = GraphNameFormatStrategy.UpperCase) + public SchemaFormatStrategy( + TypeExpressionNullabilityFormatRules nullabilityStrategy = TypeExpressionNullabilityFormatRules.Default, + SchemaItemNameFormatOptions typeNameStrategy = SchemaItemNameFormatOptions.ProperCase, + SchemaItemNameFormatOptions fieldNameStrategy = SchemaItemNameFormatOptions.CamelCase, + SchemaItemNameFormatOptions enumValueStrategy = SchemaItemNameFormatOptions.UpperCase) { this.NullabilityStrategy = nullabilityStrategy; this.GraphTypeNameStrategy = typeNameStrategy; @@ -54,33 +55,8 @@ public GraphSchemaFormatStrategy( this.EnumValueStrategy = enumValueStrategy; } - /// - /// Applies custom, programatic level formatting to the - /// target schema item. - /// - /// - /// - /// The is not yet added to the schema - /// and may not be be fully populated. - /// For example, a field will not yet have a parent assignment and graph types - /// will not have any rendered fields when this method is processed. - /// - /// - /// Schema items are immutable for any essential data values. Use the various - /// implementations of .Clone() to create copies of a schema item with - /// updated values. - /// - /// - /// Applied changes are accepted as is with no further validation, ensure your - /// formats are consistant with the target schema's expectations. - /// - /// - /// The type of schema item being formatted. - /// The complete configuration settings setup - /// at application start for the target schema. - /// The schema item to apply formatting rules to. - /// The updated or altered schema item instance that was formatted. - public virtual T ApplyFormatting(ISchemaConfiguration configuration, T schemaItem) + /// + public T ApplySchemaItemRules(ISchemaConfiguration configuration, T schemaItem) where T : ISchemaItem { switch (schemaItem) @@ -110,39 +86,72 @@ public virtual T ApplyFormatting(ISchemaConfiguration configuration, T schema return schemaItem; } + /// + public string FormatSchemaItemName(string name, NameFormatCategory target) + { + switch (target) + { + case NameFormatCategory.GraphType: + + // enforce non-renaming standards. this is not overrideable a child object. + if (!GlobalTypes.CanBeRenamed(name)) + return name; + + return this.FormatGraphTypeName(name); + + case NameFormatCategory.Field: + return this.FormatFieldName(name); + + case NameFormatCategory.EnumValue: + return this.FormatEnumValueName(name); + + case NameFormatCategory.Directive: + return this.FormatDirectiveName(name); + + default: + throw new InvalidOperationException($"Unknown {nameof(NameFormatCategory)} target: '{target}'"); + } + } + /// - /// Formats a field or argument name according to the strategy declared on this formatter. + /// Formats the directive name to be how it will appear in a query, excluding the '@' + /// symbol. + /// + /// The directive name to format. + /// System.String. + protected virtual string FormatDirectiveName(string name) + { + return this.FormatName(name, this.FieldNameStrategy); + } + + /// + /// Formats a field or argument name according to the rules declared on this strategy. /// /// The field or argument name to format. /// System.String. - public virtual string FormatFieldName(string name) + protected virtual string FormatFieldName(string name) { - return this.FormatName(name, FieldNameStrategy); + return this.FormatName(name, this.FieldNameStrategy); } /// - /// Formats the enum value name according to the strategy declared on this formatter. + /// Formats the enum value name according to the rules declared on this strategy. /// /// The enum value name to format. /// System.String. - public virtual string FormatEnumValueName(string name) + protected virtual string FormatEnumValueName(string name) { - return this.FormatName(name, EnumValueStrategy); + return this.FormatName(name, this.EnumValueStrategy); } /// - /// Formats the graph type name according to the strategy declared on this formatter. + /// Formats the graph type name according to the rules declared on this strategy. /// /// The type name to format. /// System.String. - public virtual string FormatGraphTypeName(string name) + protected virtual string FormatGraphTypeName(string name) { - // enforce non-renaming standards in the maker since the - // directly controls the formatter - if (!GlobalTypes.CanBeRenamed(name)) - return name; - - return this.FormatName(name, GraphTypeNameStrategy); + return this.FormatName(name, this.GraphTypeNameStrategy); } /// @@ -169,7 +178,7 @@ protected virtual IUnionGraphType FormatUnionType(ISchemaConfiguration configura /// IDirective. protected virtual IEnumValue FormatEnumValue(ISchemaConfiguration configuration, IEnumValue enumValue) { - var formattedName = this.FormatEnumValueName(enumValue.Name); + var formattedName = this.FormatSchemaItemName(enumValue.Name, NameFormatCategory.EnumValue); return enumValue.Clone(valueName: formattedName); } @@ -184,13 +193,13 @@ protected virtual IEnumValue FormatEnumValue(ISchemaConfiguration configuration, protected virtual IDirective FormatDirective(ISchemaConfiguration configuration, IDirective directive) { // directives are referenced as fields - var formattedName = this.FormatFieldName(directive.Name); + var formattedName = this.FormatSchemaItemName(directive.Name, NameFormatCategory.Field); return (IDirective)directive.Clone(formattedName); } /// - /// Applies the schema specific formats identified by - /// to the target . + /// Applies the schema specific formats identified by + /// to the target . /// /// The configuration to read formatting settings /// from. @@ -204,7 +213,7 @@ protected virtual IGraphType FormatGraphType(ISchemaConfiguration configuration, return graphType; } - var formattedName = this.FormatGraphTypeName(graphType.Name); + var formattedName = this.FormatSchemaItemName(graphType.Name, NameFormatCategory.GraphType); return graphType.Clone(formattedName); } @@ -219,13 +228,11 @@ protected virtual IGraphType FormatGraphType(ISchemaConfiguration configuration, protected virtual IInputGraphField FormatInputGraphField(ISchemaConfiguration configuration, IInputGraphField inputGraphField) { if (!inputGraphField.TypeExpression.IsFixed) - { - inputGraphField = this.ApplyTypeExpressionNullabilityStrategy(inputGraphField); - } + inputGraphField = this.ApplyTypeExpressionNullabilityRules(inputGraphField); - var formattedName = this.FormatFieldName(inputGraphField.Name); + var formattedName = this.FormatSchemaItemName(inputGraphField.Name, NameFormatCategory.Field); var typeExpression = inputGraphField.TypeExpression; - typeExpression = typeExpression.Clone(this.FormatGraphTypeName(typeExpression.TypeName)); + typeExpression = typeExpression.Clone(this.FormatSchemaItemName(typeExpression.TypeName, NameFormatCategory.GraphType)); return inputGraphField.Clone(fieldName: formattedName, typeExpression: typeExpression); } @@ -241,11 +248,11 @@ protected virtual IInputGraphField FormatInputGraphField(ISchemaConfiguration co protected virtual IGraphField FormatGraphField(ISchemaConfiguration configuration, IGraphField graphField) { if (!graphField.TypeExpression.IsFixed) - graphField = this.ApplyTypeExpressionNullabilityStrategy(graphField); + graphField = this.ApplyTypeExpressionNullabilityRules(graphField); - var formattedName = this.FormatFieldName(graphField.Name); + var formattedName = this.FormatSchemaItemName(graphField.Name, NameFormatCategory.Field); var typeExpression = graphField.TypeExpression; - typeExpression = typeExpression.Clone(this.FormatGraphTypeName(typeExpression.TypeName)); + typeExpression = typeExpression.Clone(this.FormatSchemaItemName(typeExpression.TypeName, NameFormatCategory.GraphType)); return graphField.Clone(fieldName: formattedName, typeExpression: typeExpression); } @@ -261,11 +268,11 @@ protected virtual IGraphField FormatGraphField(ISchemaConfiguration configuratio protected virtual IGraphArgument FormatArgument(ISchemaConfiguration configuration, IGraphArgument argument) { if (!argument.TypeExpression.IsFixed) - argument = this.ApplyTypeExpressionNullabilityStrategy(argument); + argument = this.ApplyTypeExpressionNullabilityRules(argument); - var formattedName = this.FormatFieldName(argument.Name); + var formattedName = this.FormatSchemaItemName(argument.Name, NameFormatCategory.Field); var typeExpression = argument.TypeExpression; - typeExpression = typeExpression.Clone(this.FormatGraphTypeName(typeExpression.TypeName)); + typeExpression = typeExpression.Clone(this.FormatSchemaItemName(typeExpression.TypeName, NameFormatCategory.GraphType)); return argument.Clone(argumentName: formattedName, typeExpression: typeExpression); } @@ -277,21 +284,21 @@ protected virtual IGraphArgument FormatArgument(ISchemaConfiguration configurati /// /// The graph field to update. /// IGraphField. - protected virtual IGraphField ApplyTypeExpressionNullabilityStrategy(IGraphField graphField) + protected virtual IGraphField ApplyTypeExpressionNullabilityRules(IGraphField graphField) { GraphTypeExpressionNullabilityStrategies strat = GraphTypeExpressionNullabilityStrategies.None; var shouldBeNonNullType = this.NullabilityStrategy - .HasFlag(NullabilityFormatStrategy.NonNullTemplates) + .HasFlag(TypeExpressionNullabilityFormatRules.NonNullIntermediateTypes) && graphField.IsVirtual; shouldBeNonNullType = shouldBeNonNullType || (this.NullabilityStrategy - .HasFlag(NullabilityFormatStrategy.NonNullOutputStrings) + .HasFlag(TypeExpressionNullabilityFormatRules.NonNullOutputStrings) && graphField.ObjectType == typeof(string)); shouldBeNonNullType = shouldBeNonNullType || (this.NullabilityStrategy - .HasFlag(NullabilityFormatStrategy.NonNullReferenceTypes) + .HasFlag(TypeExpressionNullabilityFormatRules.NonNullReferenceTypes) && !graphField.IsVirtual && graphField.ObjectType != typeof(string) && !graphField.ObjectType.IsValueType); @@ -299,7 +306,7 @@ protected virtual IGraphField ApplyTypeExpressionNullabilityStrategy(IGraphField if (shouldBeNonNullType) strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullType; - if (this.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullLists)) + if (this.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullLists)) strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullLists; var newTypeExpression = graphField.TypeExpression.Clone(strat); @@ -313,23 +320,23 @@ protected virtual IGraphField ApplyTypeExpressionNullabilityStrategy(IGraphField /// /// The graph field to update. /// IGraphField. - protected virtual IInputGraphField ApplyTypeExpressionNullabilityStrategy(IInputGraphField inputGraphField) + protected virtual IInputGraphField ApplyTypeExpressionNullabilityRules(IInputGraphField inputGraphField) { GraphTypeExpressionNullabilityStrategies strat = GraphTypeExpressionNullabilityStrategies.None; var shouldTypeBeNonNull = this.NullabilityStrategy - .HasFlag(NullabilityFormatStrategy.NonNullInputStrings) + .HasFlag(TypeExpressionNullabilityFormatRules.NonNullInputStrings) && inputGraphField.ObjectType == typeof(string); shouldTypeBeNonNull = shouldTypeBeNonNull || - (this.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullReferenceTypes) + (this.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullReferenceTypes) && inputGraphField.ObjectType != typeof(string) && !inputGraphField.ObjectType.IsValueType); if (shouldTypeBeNonNull) strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullType; - if (this.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullLists)) + if (this.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullLists)) strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullLists; var newTypeExpression = inputGraphField.TypeExpression.Clone(strat); @@ -353,24 +360,24 @@ protected virtual IInputGraphField ApplyTypeExpressionNullabilityStrategy(IInput /// /// The argument to update. /// IGraphField. - protected virtual IGraphArgument ApplyTypeExpressionNullabilityStrategy(IGraphArgument argument) + protected virtual IGraphArgument ApplyTypeExpressionNullabilityRules(IGraphArgument argument) { GraphTypeExpressionNullabilityStrategies strat = GraphTypeExpressionNullabilityStrategies.None; var shouldBeNonNullType = this.NullabilityStrategy - .HasFlag(NullabilityFormatStrategy.NonNullInputStrings) + .HasFlag(TypeExpressionNullabilityFormatRules.NonNullInputStrings) && argument.ObjectType == typeof(string); shouldBeNonNullType = shouldBeNonNullType || (this.NullabilityStrategy - .HasFlag(NullabilityFormatStrategy.NonNullReferenceTypes) + .HasFlag(TypeExpressionNullabilityFormatRules.NonNullReferenceTypes) && argument.ObjectType != typeof(string) && !argument.ObjectType.IsValueType); if (shouldBeNonNullType) strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullType; - if (this.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullLists)) + if (this.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullLists)) strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullLists; var newTypeExpression = argument.TypeExpression.Clone(strat); @@ -393,27 +400,27 @@ protected virtual IGraphArgument ApplyTypeExpressionNullabilityStrategy(IGraphAr /// The name value being formatted. /// The selected strategy to format with. /// System.String. - protected virtual string FormatName(string name, GraphNameFormatStrategy strategy) + protected virtual string FormatName(string name, SchemaItemNameFormatOptions strategy) { if (name == null) return null; switch (strategy) { - case GraphNameFormatStrategy.ProperCase: + case SchemaItemNameFormatOptions.ProperCase: return name.FirstCharacterToUpperInvariant(); - case GraphNameFormatStrategy.CamelCase: + case SchemaItemNameFormatOptions.CamelCase: return name.FirstCharacterToLowerInvariant(); - case GraphNameFormatStrategy.UpperCase: + case SchemaItemNameFormatOptions.UpperCase: return name.ToUpperInvariant(); - case GraphNameFormatStrategy.LowerCase: + case SchemaItemNameFormatOptions.LowerCase: return name.ToLowerInvariant(); // ReSharper disable once RedundantCaseLabel - case GraphNameFormatStrategy.NoChanges: + case SchemaItemNameFormatOptions.NoChanges: default: return name; } @@ -424,19 +431,19 @@ protected virtual string FormatName(string name, GraphNameFormatStrategy strateg /// to apply to field and argument type expressions. /// /// The nullability strategy. - public NullabilityFormatStrategy NullabilityStrategy { get; set; } + public TypeExpressionNullabilityFormatRules NullabilityStrategy { get; set; } /// /// Gets or sets the name format strategy to use for graph type names. /// /// The type name strategy. - public GraphNameFormatStrategy GraphTypeNameStrategy { get; set; } + public SchemaItemNameFormatOptions GraphTypeNameStrategy { get; set; } /// /// Gets or sets the name format strategy to use for field names. /// /// The field strategy. - public GraphNameFormatStrategy FieldNameStrategy { get; set; } + public SchemaItemNameFormatOptions FieldNameStrategy { get; set; } /// /// Gets or sets the name format strategy to use for enum values. @@ -446,6 +453,6 @@ protected virtual string FormatName(string name, GraphNameFormatStrategy strateg /// the . /// /// The enum value name strategy. - public GraphNameFormatStrategy EnumValueStrategy { get; set; } + public SchemaItemNameFormatOptions EnumValueStrategy { get; set; } } } \ No newline at end of file diff --git a/src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategyBuilder.cs b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategyBuilder.cs similarity index 70% rename from src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategyBuilder.cs rename to src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategyBuilder.cs index 4aa430232..3d3f4073d 100644 --- a/src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategyBuilder.cs +++ b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategyBuilder.cs @@ -9,29 +9,31 @@ namespace GraphQL.AspNet.Configuration.Formatting { + using GraphQL.AspNet.Interfaces.Configuration; + /// - /// A builder class to formulate a + /// A builder class to formulate a /// with various options. /// - public class GraphSchemaFormatStrategyBuilder + public class SchemaFormatStrategyBuilder { /// /// Starts a new strategy builder instance. /// /// SchemaFormatStrategyBuilder. - public static GraphSchemaFormatStrategyBuilder Create() + public static SchemaFormatStrategyBuilder Create() { - return new GraphSchemaFormatStrategyBuilder(); + return new SchemaFormatStrategyBuilder(); } - private readonly GraphSchemaFormatStrategy _format; + private readonly SchemaFormatStrategy _format; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public GraphSchemaFormatStrategyBuilder() + public SchemaFormatStrategyBuilder() { - _format = new GraphSchemaFormatStrategy(); + _format = new SchemaFormatStrategy(); } /// @@ -40,7 +42,7 @@ public GraphSchemaFormatStrategyBuilder() /// per argument basis. /// /// SchemaFormatStrategyBuilder. - public GraphSchemaFormatStrategyBuilder WithRequiredStrings() + public SchemaFormatStrategyBuilder WithRequiredStrings() { return this.WithRequiredInputStrings() .WithRequiredOutputStrings(); @@ -52,10 +54,10 @@ public GraphSchemaFormatStrategyBuilder WithRequiredStrings() /// per argument basis. /// /// GraphSchemaFormatStrategyBuilder. - public GraphSchemaFormatStrategyBuilder WithRequiredInputStrings() + public SchemaFormatStrategyBuilder WithRequiredInputStrings() { - if (!_format.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullInputStrings)) - _format.NullabilityStrategy = _format.NullabilityStrategy | NullabilityFormatStrategy.NonNullInputStrings; + if (!_format.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullInputStrings)) + _format.NullabilityStrategy = _format.NullabilityStrategy | TypeExpressionNullabilityFormatRules.NonNullInputStrings; return this; } @@ -66,10 +68,10 @@ public GraphSchemaFormatStrategyBuilder WithRequiredInputStrings() /// per argument basis. /// /// GraphSchemaFormatStrategyBuilder. - public GraphSchemaFormatStrategyBuilder WithRequiredOutputStrings() + public SchemaFormatStrategyBuilder WithRequiredOutputStrings() { - if (!_format.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullOutputStrings)) - _format.NullabilityStrategy = _format.NullabilityStrategy | NullabilityFormatStrategy.NonNullOutputStrings; + if (!_format.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullOutputStrings)) + _format.NullabilityStrategy = _format.NullabilityStrategy | TypeExpressionNullabilityFormatRules.NonNullOutputStrings; return this; } @@ -80,10 +82,10 @@ public GraphSchemaFormatStrategyBuilder WithRequiredOutputStrings() /// per argument basis. /// /// SchemaFormatStrategyBuilder. - public GraphSchemaFormatStrategyBuilder WithRequiredLists() + public SchemaFormatStrategyBuilder WithRequiredLists() { - if (!_format.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullLists)) - _format.NullabilityStrategy = _format.NullabilityStrategy | NullabilityFormatStrategy.NonNullLists; + if (!_format.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullLists)) + _format.NullabilityStrategy = _format.NullabilityStrategy | TypeExpressionNullabilityFormatRules.NonNullLists; return this; } @@ -94,10 +96,10 @@ public GraphSchemaFormatStrategyBuilder WithRequiredLists() /// per argument basis. /// /// SchemaFormatStrategyBuilder. - public GraphSchemaFormatStrategyBuilder WithRequiredObjects() + public SchemaFormatStrategyBuilder WithRequiredObjects() { - if (!_format.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullReferenceTypes)) - _format.NullabilityStrategy = _format.NullabilityStrategy | NullabilityFormatStrategy.NonNullReferenceTypes; + if (!_format.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullReferenceTypes)) + _format.NullabilityStrategy = _format.NullabilityStrategy | TypeExpressionNullabilityFormatRules.NonNullReferenceTypes; return this; } @@ -108,9 +110,9 @@ public GraphSchemaFormatStrategyBuilder WithRequiredObjects() /// as nullable. /// /// SchemaFormatStrategyBuilder. - public GraphSchemaFormatStrategyBuilder ClearNullabilityRules() + public SchemaFormatStrategyBuilder ClearNullabilityRules() { - _format.NullabilityStrategy = NullabilityFormatStrategy.None; + _format.NullabilityStrategy = TypeExpressionNullabilityFormatRules.None; return this; } @@ -122,7 +124,7 @@ public GraphSchemaFormatStrategyBuilder ClearNullabilityRules() /// /// The strategy to employ for graph type names. /// GraphSchemaFormatStrategyBuilder. - public GraphSchemaFormatStrategyBuilder WithGraphTypeNameFormat(GraphNameFormatStrategy strategy) + public SchemaFormatStrategyBuilder WithGraphTypeNameFormat(SchemaItemNameFormatOptions strategy) { _format.GraphTypeNameStrategy = strategy; return this; @@ -136,7 +138,7 @@ public GraphSchemaFormatStrategyBuilder WithGraphTypeNameFormat(GraphNameFormatS /// /// The strategy to employ for field names. /// GraphSchemaFormatStrategyBuilder. - public GraphSchemaFormatStrategyBuilder WithFieldNameFormat(GraphNameFormatStrategy strategy) + public SchemaFormatStrategyBuilder WithFieldNameFormat(SchemaItemNameFormatOptions strategy) { _format.FieldNameStrategy = strategy; return this; @@ -150,7 +152,7 @@ public GraphSchemaFormatStrategyBuilder WithFieldNameFormat(GraphNameFormatStrat /// /// The strategy to employ for graph type names. /// GraphSchemaFormatStrategyBuilder. - public GraphSchemaFormatStrategyBuilder WithEnumValueFormat(GraphNameFormatStrategy strategy) + public SchemaFormatStrategyBuilder WithEnumValueFormat(SchemaItemNameFormatOptions strategy) { _format.EnumValueStrategy = strategy; return this; @@ -160,7 +162,7 @@ public GraphSchemaFormatStrategyBuilder WithEnumValueFormat(GraphNameFormatStrat /// Returns the format strategy instance being built. /// /// GraphSchemaFormatStrategy. - public GraphSchemaFormatStrategy Build() + public ISchemaFormatStrategy Build() { return _format; } diff --git a/src/graphql-aspnet/Configuration/Formatting/GraphNameFormatStrategy.cs b/src/graphql-aspnet/Configuration/Formatting/SchemaItemNameFormatOptions.cs similarity index 93% rename from src/graphql-aspnet/Configuration/Formatting/GraphNameFormatStrategy.cs rename to src/graphql-aspnet/Configuration/Formatting/SchemaItemNameFormatOptions.cs index 28690dcaf..95c8662bb 100644 --- a/src/graphql-aspnet/Configuration/Formatting/GraphNameFormatStrategy.cs +++ b/src/graphql-aspnet/Configuration/Formatting/SchemaItemNameFormatOptions.cs @@ -13,7 +13,7 @@ namespace GraphQL.AspNet.Configuration.Formatting /// A set of internally supported name formatting options for various names and values created /// for a schema. /// - public enum GraphNameFormatStrategy + public enum SchemaItemNameFormatOptions { ProperCase, CamelCase, diff --git a/src/graphql-aspnet/Configuration/Formatting/GraphTypeNullabilityStrategy.cs b/src/graphql-aspnet/Configuration/Formatting/TypeExpressionNullabilityFormatRules.cs similarity index 92% rename from src/graphql-aspnet/Configuration/Formatting/GraphTypeNullabilityStrategy.cs rename to src/graphql-aspnet/Configuration/Formatting/TypeExpressionNullabilityFormatRules.cs index 4984935fc..e17554cc7 100644 --- a/src/graphql-aspnet/Configuration/Formatting/GraphTypeNullabilityStrategy.cs +++ b/src/graphql-aspnet/Configuration/Formatting/TypeExpressionNullabilityFormatRules.cs @@ -15,7 +15,7 @@ namespace GraphQL.AspNet.Configuration.Formatting /// A bitwise set of format strategies that can be applied out of the box. /// [Flags] - public enum NullabilityFormatStrategy + public enum TypeExpressionNullabilityFormatRules { /// /// No changes are made to the nullability of different fields. They are used as provided in @@ -30,7 +30,7 @@ public enum NullabilityFormatStrategy /// in reducing null-handling noise in various client side code generators. Intermediate graph /// types will never be null but may be expressed as "nullable" in the schema if this is not set. /// - NonNullTemplates = 1, + NonNullIntermediateTypes = 1, /// /// All lists, in any type expression, be that as input arguments @@ -52,7 +52,7 @@ public enum NullabilityFormatStrategy /// /// The schema will treat all class reference types, in any type expression, as being non-nullable by - /// default. + /// default. This option DOES NOT include the type. /// NonNullReferenceTypes = 16, @@ -67,6 +67,6 @@ public enum NullabilityFormatStrategy /// the source code. All strings and reference types passed to an argument or returned /// from a field are considered nullable unless otherwise overriden. /// - Default = NonNullTemplates, + Default = NonNullIntermediateTypes, } } \ No newline at end of file diff --git a/src/graphql-aspnet/Configuration/SchemaDeclarationConfiguration.cs b/src/graphql-aspnet/Configuration/SchemaDeclarationConfiguration.cs index 41840758a..45f0c4a31 100644 --- a/src/graphql-aspnet/Configuration/SchemaDeclarationConfiguration.cs +++ b/src/graphql-aspnet/Configuration/SchemaDeclarationConfiguration.cs @@ -46,7 +46,6 @@ public void Merge(ISchemaDeclarationConfiguration config) this.DisableIntrospection = config.DisableIntrospection; this.FieldDeclarationRequirements = config.FieldDeclarationRequirements; this.SchemaFormatStrategy = config.SchemaFormatStrategy; - this.ArgumentBindingRule = config.ArgumentBindingRule; if (config.AllowedOperations != null) @@ -66,7 +65,7 @@ public void Merge(ISchemaDeclarationConfiguration config) public TemplateDeclarationRequirements FieldDeclarationRequirements { get; set; } = TemplateDeclarationRequirements.Default; /// - public GraphSchemaFormatStrategy SchemaFormatStrategy { get; set; } = new GraphSchemaFormatStrategy(); + public ISchemaFormatStrategy SchemaFormatStrategy { get; set; } = new SchemaFormatStrategy(); /// public HashSet AllowedOperations { get; } diff --git a/src/graphql-aspnet/Engine/DefaultGraphQLSchemaFactory_Controllers.cs b/src/graphql-aspnet/Engine/DefaultGraphQLSchemaFactory_Controllers.cs index 29aa72a0e..4710310c5 100644 --- a/src/graphql-aspnet/Engine/DefaultGraphQLSchemaFactory_Controllers.cs +++ b/src/graphql-aspnet/Engine/DefaultGraphQLSchemaFactory_Controllers.cs @@ -12,6 +12,7 @@ namespace GraphQL.AspNet.Engine using System; using System.Collections.Generic; using GraphQL.AspNet.Common; + using GraphQL.AspNet.Configuration.Formatting; using GraphQL.AspNet.Execution; using GraphQL.AspNet.Execution.Exceptions; using GraphQL.AspNet.Interfaces.Internal; @@ -106,7 +107,7 @@ protected virtual IObjectGraphType AddOrRetrieveVirtualTypeOwner(IGraphFieldTemp .Configuration .DeclarationOptions .SchemaFormatStrategy - .FormatFieldName(segment.Name); + .FormatSchemaItemName(segment.Name, NameFormatCategory.Field); if (parentType.Fields.ContainsKey(formattedName)) { @@ -182,7 +183,7 @@ protected virtual IObjectGraphType CreateVirtualFieldOnParent( .Configuration .DeclarationOptions .SchemaFormatStrategy? - .ApplyFormatting( + .ApplySchemaItemRules( this.Schema.Configuration, childField) ?? childField; @@ -195,7 +196,7 @@ protected virtual IObjectGraphType CreateVirtualFieldOnParent( .Configuration .DeclarationOptions .SchemaFormatStrategy? - .ApplyFormatting( + .ApplySchemaItemRules( this.Schema.Configuration, graphType) ?? graphType; @@ -230,7 +231,7 @@ protected virtual string MakeSafeTypeNameFromItemPath(ItemPath path) break; default: - segments.Add(this.Schema.Configuration.DeclarationOptions.SchemaFormatStrategy.FormatGraphTypeName(pathSegmentName)); + segments.Add(this.Schema.Configuration.DeclarationOptions.SchemaFormatStrategy.FormatSchemaItemName(pathSegmentName, NameFormatCategory.GraphType)); break; } } @@ -258,7 +259,7 @@ protected virtual void AddActionAsField(IObjectGraphType parentType, IGraphField .Configuration .DeclarationOptions .SchemaFormatStrategy? - .ApplyFormatting( + .ApplySchemaItemRules( this.Schema.Configuration, fieldResult.Field) ?? fieldResult.Field; diff --git a/src/graphql-aspnet/Execution/Response/ResponseWriterBase.cs b/src/graphql-aspnet/Execution/Response/ResponseWriterBase.cs index 18cfb8eb4..da92076ed 100644 --- a/src/graphql-aspnet/Execution/Response/ResponseWriterBase.cs +++ b/src/graphql-aspnet/Execution/Response/ResponseWriterBase.cs @@ -14,11 +14,12 @@ namespace GraphQL.AspNet.Execution.Response using System.Text.Json; using GraphQL.AspNet.Common; using GraphQL.AspNet.Common.Extensions; - using GraphQL.AspNet.Execution.Source; using GraphQL.AspNet.Configuration.Formatting; + using GraphQL.AspNet.Execution.Source; + using GraphQL.AspNet.Interfaces.Configuration; using GraphQL.AspNet.Interfaces.Execution; - using GraphQL.AspNet.Interfaces.Schema; using GraphQL.AspNet.Interfaces.Execution.Response; + using GraphQL.AspNet.Interfaces.Schema; /// /// A class containing many shared methods for writing all or part of a @@ -248,7 +249,7 @@ protected virtual void WriteLeafValue(Utf8JsonWriter writer, object value, bool } if (value.GetType().IsEnum) - value = this.Formatter.FormatEnumValueName(value.ToString()); + value = this.Formatter.FormatSchemaItemName(value.ToString(), NameFormatCategory.EnumValue); switch (value) { @@ -374,7 +375,7 @@ protected virtual void WritePreEncodedString(Utf8JsonWriter writer, string prope /// Gets the formatter used when writing graph names to the stream. /// /// The name formatter. - protected virtual GraphSchemaFormatStrategy Formatter { get; } + protected virtual ISchemaFormatStrategy Formatter { get; } /// /// Gets a set of settings to use whenever the serializer needs to be directly invoked. diff --git a/src/graphql-aspnet/Interfaces/Configuration/ISchemaDeclarationConfiguration.cs b/src/graphql-aspnet/Interfaces/Configuration/ISchemaDeclarationConfiguration.cs index d6ec9ed9f..445d709a7 100644 --- a/src/graphql-aspnet/Interfaces/Configuration/ISchemaDeclarationConfiguration.cs +++ b/src/graphql-aspnet/Interfaces/Configuration/ISchemaDeclarationConfiguration.cs @@ -12,7 +12,6 @@ namespace GraphQL.AspNet.Interfaces.Configuration using System.Collections.Generic; using GraphQL.AspNet.Attributes; using GraphQL.AspNet.Configuration; - using GraphQL.AspNet.Configuration.Formatting; using GraphQL.AspNet.Interfaces.Schema; using GraphQL.AspNet.Schemas.TypeSystem; @@ -49,10 +48,10 @@ public interface ISchemaDeclarationConfiguration /// /// Gets an instance of a format strategy object that will apply custom - /// formats and other updates to a newly created schema item just before its added to a schema. + /// formats and other rules to a newly created schema item just before its added to a schema. /// /// The schema item formatter. - GraphSchemaFormatStrategy SchemaFormatStrategy { get; } + ISchemaFormatStrategy SchemaFormatStrategy { get; } /// /// Gets the set of operation types that can be registered to this schema. diff --git a/src/graphql-aspnet/Interfaces/Configuration/ISchemaFormatStrategy.cs b/src/graphql-aspnet/Interfaces/Configuration/ISchemaFormatStrategy.cs new file mode 100644 index 000000000..4b14d6297 --- /dev/null +++ b/src/graphql-aspnet/Interfaces/Configuration/ISchemaFormatStrategy.cs @@ -0,0 +1,59 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Interfaces.Configuration +{ + using GraphQL.AspNet.Configuration.Formatting; + using GraphQL.AspNet.Interfaces.Schema; + + /// + /// A strategy that defines specific rules and overrides to apply to schema items as they + /// are generated and added to a schema being built. + /// + public interface ISchemaFormatStrategy + { + /// + /// Applies custom, programatic level formatting rules (name formatting, nullability standards etc.) to the + /// target schema item before it is added to the schema. + /// + /// + /// + /// The is not yet added to the schema + /// and may not be be fully populated. + /// For example, a field will not yet have a parent assignment and graph types + /// will not have any rendered fields when this method is processed. + /// + /// + /// Schema items are immutable for any essential data values. Use the various + /// implementations of .Clone() to create copies of a schema item with + /// updated values. + /// + /// + /// Applied changes are accepted as is with no further validation, ensure your + /// formats are consistant with the target schema's expectations. + /// + /// + /// The type of schema item being formatted. + /// The complete configuration settings setup + /// at application start for the target schema. + /// The schema item to apply formatting rules to. + /// The updated or altered schema item instance that was formatted. + T ApplySchemaItemRules(ISchemaConfiguration configuration, T schemaItem) + where T : ISchemaItem; + + /// + /// Formats the provided string as if it were the name of an entity + /// according to the rules of the schema. + /// + /// The name to format. + /// The target schema item entity type. + /// A formatted string. + string FormatSchemaItemName(string name, NameFormatCategory target); + } +} diff --git a/src/graphql-aspnet/Middleware/QueryExecution/Components/AuthorizeQueryOperationMiddleware.cs b/src/graphql-aspnet/Middleware/QueryExecution/Components/AuthorizeQueryOperationMiddleware.cs index 4a31c81cb..6d1c0b8a9 100644 --- a/src/graphql-aspnet/Middleware/QueryExecution/Components/AuthorizeQueryOperationMiddleware.cs +++ b/src/graphql-aspnet/Middleware/QueryExecution/Components/AuthorizeQueryOperationMiddleware.cs @@ -61,6 +61,7 @@ private async Task AuthorizeOperationAsync(QueryExecutionContext context, { var authTasks = new List(); bool isAuthorized = true; + for (var i = 0; i < context.Operation.SecureItems.Count; i++) { var securePart = context.Operation.SecureItems[i]; diff --git a/src/graphql-aspnet/Schemas/Generation/TypeMakers/DirectiveMaker.cs b/src/graphql-aspnet/Schemas/Generation/TypeMakers/DirectiveMaker.cs index 89725a121..5fc602c80 100644 --- a/src/graphql-aspnet/Schemas/Generation/TypeMakers/DirectiveMaker.cs +++ b/src/graphql-aspnet/Schemas/Generation/TypeMakers/DirectiveMaker.cs @@ -73,7 +73,7 @@ public virtual GraphTypeCreationResult CreateGraphType(IGraphTypeTemplate typeTe .Configuration? .DeclarationOptions? .SchemaFormatStrategy? - .ApplyFormatting(_schema.Configuration, directive) ?? directive; + .ApplySchemaItemRules(_schema.Configuration, directive) ?? directive; // all arguments are required to have the same signature via validation // can use any method to fill the arg field list diff --git a/src/graphql-aspnet/Schemas/Generation/TypeMakers/EnumGraphTypeMaker.cs b/src/graphql-aspnet/Schemas/Generation/TypeMakers/EnumGraphTypeMaker.cs index 7861d2035..b8ebf6112 100644 --- a/src/graphql-aspnet/Schemas/Generation/TypeMakers/EnumGraphTypeMaker.cs +++ b/src/graphql-aspnet/Schemas/Generation/TypeMakers/EnumGraphTypeMaker.cs @@ -66,7 +66,7 @@ public virtual GraphTypeCreationResult CreateGraphType(IGraphTypeTemplate typeTe graphType = _config .DeclarationOptions? .SchemaFormatStrategy? - .ApplyFormatting(_config, graphType) ?? graphType; + .ApplySchemaItemRules(_config, graphType) ?? graphType; var result = new GraphTypeCreationResult() { @@ -100,7 +100,7 @@ public virtual GraphTypeCreationResult CreateGraphType(IGraphTypeTemplate typeTe valueOption = _config .DeclarationOptions? .SchemaFormatStrategy? - .ApplyFormatting(_config, valueOption) ?? valueOption; + .ApplySchemaItemRules(_config, valueOption) ?? valueOption; if (Constants.QueryLanguage.IsReservedKeyword(valueOption.Name)) { diff --git a/src/graphql-aspnet/Schemas/Generation/TypeMakers/GraphArgumentMaker.cs b/src/graphql-aspnet/Schemas/Generation/TypeMakers/GraphArgumentMaker.cs index f079b0cf5..d63abe852 100644 --- a/src/graphql-aspnet/Schemas/Generation/TypeMakers/GraphArgumentMaker.cs +++ b/src/graphql-aspnet/Schemas/Generation/TypeMakers/GraphArgumentMaker.cs @@ -83,7 +83,7 @@ public GraphArgumentCreationResult CreateArgument(ISchemaItem owner, IGraphArgum argument = _config .DeclarationOptions? .SchemaFormatStrategy? - .ApplyFormatting(_config, argument) ?? argument; + .ApplySchemaItemRules(_config, argument) ?? argument; var result = new GraphArgumentCreationResult(); result.Argument = argument; diff --git a/src/graphql-aspnet/Schemas/Generation/TypeMakers/GraphFieldMaker.cs b/src/graphql-aspnet/Schemas/Generation/TypeMakers/GraphFieldMaker.cs index 2344db94f..a5a473f68 100644 --- a/src/graphql-aspnet/Schemas/Generation/TypeMakers/GraphFieldMaker.cs +++ b/src/graphql-aspnet/Schemas/Generation/TypeMakers/GraphFieldMaker.cs @@ -69,7 +69,7 @@ public virtual GraphFieldCreationResult CreateField(IGraphFieldTemp field = _config .DeclarationOptions .SchemaFormatStrategy? - .ApplyFormatting(_config, field) ?? field; + .ApplySchemaItemRules(_config, field) ?? field; field.Description = template.Description; field.Complexity = template.Complexity; @@ -139,7 +139,7 @@ public GraphFieldCreationResult CreateField(IInputGraphFieldTe field = _config .DeclarationOptions .SchemaFormatStrategy? - .ApplyFormatting(_config, field) ?? field; + .ApplySchemaItemRules(_config, field) ?? field; result.AddDependentRange(template.RetrieveRequiredTypes()); diff --git a/src/graphql-aspnet/Schemas/Generation/TypeMakers/InputObjectGraphTypeMaker.cs b/src/graphql-aspnet/Schemas/Generation/TypeMakers/InputObjectGraphTypeMaker.cs index 8174ef92e..69d5027aa 100644 --- a/src/graphql-aspnet/Schemas/Generation/TypeMakers/InputObjectGraphTypeMaker.cs +++ b/src/graphql-aspnet/Schemas/Generation/TypeMakers/InputObjectGraphTypeMaker.cs @@ -70,7 +70,7 @@ public virtual GraphTypeCreationResult CreateGraphType(IGraphTypeTemplate typeTe inputObjectType = _config .DeclarationOptions? .SchemaFormatStrategy? - .ApplyFormatting(_config, inputObjectType) ?? inputObjectType; + .ApplySchemaItemRules(_config, inputObjectType) ?? inputObjectType; // account for any potential type system directives result.AddDependentRange(template.RetrieveRequiredTypes()); diff --git a/src/graphql-aspnet/Schemas/Generation/TypeMakers/InterfaceGraphTypeMaker.cs b/src/graphql-aspnet/Schemas/Generation/TypeMakers/InterfaceGraphTypeMaker.cs index ad6c1ed86..94fe0f2d9 100644 --- a/src/graphql-aspnet/Schemas/Generation/TypeMakers/InterfaceGraphTypeMaker.cs +++ b/src/graphql-aspnet/Schemas/Generation/TypeMakers/InterfaceGraphTypeMaker.cs @@ -73,7 +73,7 @@ public virtual GraphTypeCreationResult CreateGraphType(IGraphTypeTemplate typeTe interfaceType = _config .DeclarationOptions? .SchemaFormatStrategy? - .ApplyFormatting(_config, interfaceType) ?? interfaceType; + .ApplySchemaItemRules(_config, interfaceType) ?? interfaceType; // account for any potential type system directives result.AddDependentRange(template.RetrieveRequiredTypes()); diff --git a/src/graphql-aspnet/Schemas/Generation/TypeMakers/ObjectGraphTypeMaker.cs b/src/graphql-aspnet/Schemas/Generation/TypeMakers/ObjectGraphTypeMaker.cs index 5f82f79d0..ea736f170 100644 --- a/src/graphql-aspnet/Schemas/Generation/TypeMakers/ObjectGraphTypeMaker.cs +++ b/src/graphql-aspnet/Schemas/Generation/TypeMakers/ObjectGraphTypeMaker.cs @@ -77,7 +77,7 @@ public virtual GraphTypeCreationResult CreateGraphType(IGraphTypeTemplate typeTe objectType = _config .DeclarationOptions? .SchemaFormatStrategy? - .ApplyFormatting(_config, objectType) ?? objectType; + .ApplySchemaItemRules(_config, objectType) ?? objectType; result.GraphType = objectType; result.ConcreteType = template.ObjectType; diff --git a/src/graphql-aspnet/Schemas/Generation/TypeMakers/ScalarGraphTypeMaker.cs b/src/graphql-aspnet/Schemas/Generation/TypeMakers/ScalarGraphTypeMaker.cs index b55c47070..0069409ab 100644 --- a/src/graphql-aspnet/Schemas/Generation/TypeMakers/ScalarGraphTypeMaker.cs +++ b/src/graphql-aspnet/Schemas/Generation/TypeMakers/ScalarGraphTypeMaker.cs @@ -49,7 +49,7 @@ public GraphTypeCreationResult CreateGraphType(IGraphTypeTemplate typeTemplate) scalarType = _config .DeclarationOptions? .SchemaFormatStrategy? - .ApplyFormatting(_config, scalarType) ?? scalarType; + .ApplySchemaItemRules(_config, scalarType) ?? scalarType; var result = new GraphTypeCreationResult() { diff --git a/src/graphql-aspnet/Schemas/Generation/TypeMakers/UnionGraphTypeMaker.cs b/src/graphql-aspnet/Schemas/Generation/TypeMakers/UnionGraphTypeMaker.cs index 545ed07b2..edafae15d 100644 --- a/src/graphql-aspnet/Schemas/Generation/TypeMakers/UnionGraphTypeMaker.cs +++ b/src/graphql-aspnet/Schemas/Generation/TypeMakers/UnionGraphTypeMaker.cs @@ -88,7 +88,7 @@ public GraphTypeCreationResult CreateUnionFromProxy(IGraphUnionProxy proxy) union = _config .DeclarationOptions? .SchemaFormatStrategy? - .ApplyFormatting(_config, union) ?? union; + .ApplySchemaItemRules(_config, union) ?? union; result.GraphType = union; diff --git a/src/graphql-aspnet/Schemas/GraphTypeExpression.cs b/src/graphql-aspnet/Schemas/GraphTypeExpression.cs index 53fd07314..73b469fdc 100644 --- a/src/graphql-aspnet/Schemas/GraphTypeExpression.cs +++ b/src/graphql-aspnet/Schemas/GraphTypeExpression.cs @@ -17,7 +17,6 @@ namespace GraphQL.AspNet.Schemas using GraphQL.AspNet.Common; using GraphQL.AspNet.Execution.Parsing.Lexing.Tokens; using GraphQL.AspNet.Schemas.TypeSystem; - using Microsoft.AspNetCore.Localization; /// /// A declaration of the usage of a single graph type (with appropriate wrappers). @@ -362,8 +361,8 @@ public GraphTypeExpression UnWrapExpression(MetaGraphTypes? wrapperToRemove = nu /// or have their nullability settings changed. /// /// - /// >Non-Fixed instances represent type expressions interpreted - /// from source code. + /// Non-Fixed instances represent type expressions interpreted + /// from source code. Fixed instances represent those defined via explicit attribution. /// /// /// true if this instance is customized by the developer; otherwise, false. diff --git a/src/unit-tests/graphql-aspnet-testframework/GraphQLSchemaHelperMethods.cs b/src/unit-tests/graphql-aspnet-testframework/GraphQLSchemaHelperMethods.cs index 855282697..ed452a7ca 100644 --- a/src/unit-tests/graphql-aspnet-testframework/GraphQLSchemaHelperMethods.cs +++ b/src/unit-tests/graphql-aspnet-testframework/GraphQLSchemaHelperMethods.cs @@ -30,7 +30,7 @@ public static void SetNoAlterationConfiguration(this ISchema schema) { var declarationOptions = new SchemaDeclarationConfiguration(); declarationOptions.Merge(schema.Configuration.DeclarationOptions); - declarationOptions.SchemaFormatStrategy = new GraphSchemaFormatStrategy(GraphNameFormatStrategy.NoChanges); + declarationOptions.SchemaFormatStrategy = new SchemaFormatStrategy(SchemaItemNameFormatOptions.NoChanges); var config = new SchemaConfiguration( declarationOptions, diff --git a/src/unit-tests/graphql-aspnet-testframework/TestServerBuilder{TSchema}.cs b/src/unit-tests/graphql-aspnet-testframework/TestServerBuilder{TSchema}.cs index 357a1544f..4e08584e1 100644 --- a/src/unit-tests/graphql-aspnet-testframework/TestServerBuilder{TSchema}.cs +++ b/src/unit-tests/graphql-aspnet-testframework/TestServerBuilder{TSchema}.cs @@ -105,7 +105,7 @@ protected virtual void PerformInitialConfiguration(SchemaOptions options) if (_initialSetup.HasFlag(TestOptions.UseCodeDeclaredNames)) { - options.DeclarationOptions.SchemaFormatStrategy = new GraphSchemaFormatStrategy(GraphNameFormatStrategy.NoChanges); + options.DeclarationOptions.SchemaFormatStrategy = new SchemaFormatStrategy(SchemaItemNameFormatOptions.NoChanges); } if (_initialSetup.HasFlag(TestOptions.IncludeExceptions)) diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationFieldNamingTests.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationFieldNamingTests.cs index 0b89a633c..f0f544c53 100644 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationFieldNamingTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationFieldNamingTests.cs @@ -52,7 +52,7 @@ public async Task ProductionDefaults_UpperCaseFieldNames() builder.AddGraphQL(o => { - o.DeclarationOptions.SchemaFormatStrategy = new GraphSchemaFormatStrategy(fieldNameStrategy: GraphNameFormatStrategy.UpperCase); + o.DeclarationOptions.SchemaFormatStrategy = new SchemaFormatStrategy(fieldNameStrategy: SchemaItemNameFormatOptions.UpperCase); }); var server = builder.Build(); @@ -84,7 +84,7 @@ public async Task ProductionDefaults_LowerCaseEnumValues() builder.AddGraphQL(o => { - o.DeclarationOptions.SchemaFormatStrategy = new GraphSchemaFormatStrategy(enumValueStrategy: GraphNameFormatStrategy.LowerCase); + o.DeclarationOptions.SchemaFormatStrategy = new SchemaFormatStrategy(enumValueStrategy: SchemaItemNameFormatOptions.LowerCase); }); var server = builder.Build(); @@ -116,7 +116,7 @@ public async Task ProductionDefaults_CamelCasedTypeNames() builder.AddGraphQL(o => { - o.DeclarationOptions.SchemaFormatStrategy = new GraphSchemaFormatStrategy(typeNameStrategy: GraphNameFormatStrategy.CamelCase); + o.DeclarationOptions.SchemaFormatStrategy = new SchemaFormatStrategy(typeNameStrategy: SchemaItemNameFormatOptions.CamelCase); }); var server = builder.Build(); diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFomatStrategyTests.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFomatStrategyTests.cs index e59898634..4a05c04a7 100644 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFomatStrategyTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFomatStrategyTests.cs @@ -16,123 +16,123 @@ namespace GraphQL.AspNet.Tests.Configuration using GraphQL.AspNet.Tests.Configuration.FormatStrategyTestData; using GraphQL.AspNet.Tests.Framework; using NUnit.Framework; - using NFS = GraphQL.AspNet.Configuration.Formatting.NullabilityFormatStrategy; + using NullRules = GraphQL.AspNet.Configuration.Formatting.TypeExpressionNullabilityFormatRules; [TestFixture] public class GraphSchemaFomatStrategyTests { // value type, not fixed, object graph type - [TestCase(NFS.None, typeof(Widget), "intProp", "Int!")] - [TestCase(NFS.NonNullStrings, typeof(Widget), "intProp", "Int!")] - [TestCase(NFS.NonNullLists, typeof(Widget), "intProp", "Int!")] - [TestCase(NFS.NonNullTemplates, typeof(Widget), "intProp", "Int!")] - [TestCase(NFS.NonNullReferenceTypes, typeof(Widget), "intProp", "Int!")] + [TestCase(NullRules.None, typeof(Widget), "intProp", "Int!")] + [TestCase(NullRules.NonNullStrings, typeof(Widget), "intProp", "Int!")] + [TestCase(NullRules.NonNullLists, typeof(Widget), "intProp", "Int!")] + [TestCase(NullRules.NonNullIntermediateTypes, typeof(Widget), "intProp", "Int!")] + [TestCase(NullRules.NonNullReferenceTypes, typeof(Widget), "intProp", "Int!")] // value type, fixed as nullable, object graph type - [TestCase(NFS.None, typeof(Widget), "fixedIntPropAsNullable", "Int")] - [TestCase(NFS.NonNullStrings, typeof(Widget), "fixedIntPropAsNullable", "Int")] - [TestCase(NFS.NonNullLists, typeof(Widget), "fixedIntPropAsNullable", "Int")] - [TestCase(NFS.NonNullTemplates, typeof(Widget), "fixedIntPropAsNullable", "Int")] - [TestCase(NFS.NonNullReferenceTypes, typeof(Widget), "fixedIntPropAsNullable", "Int")] + [TestCase(NullRules.None, typeof(Widget), "fixedIntPropAsNullable", "Int")] + [TestCase(NullRules.NonNullStrings, typeof(Widget), "fixedIntPropAsNullable", "Int")] + [TestCase(NullRules.NonNullLists, typeof(Widget), "fixedIntPropAsNullable", "Int")] + [TestCase(NullRules.NonNullIntermediateTypes, typeof(Widget), "fixedIntPropAsNullable", "Int")] + [TestCase(NullRules.NonNullReferenceTypes, typeof(Widget), "fixedIntPropAsNullable", "Int")] // value type, fixed as not nullable, object graph type - [TestCase(NFS.None, typeof(Widget), "fixedIntPropAsNotNullable", "Int!")] - [TestCase(NFS.NonNullStrings, typeof(Widget), "fixedIntPropAsNotNullable", "Int!")] - [TestCase(NFS.NonNullLists, typeof(Widget), "fixedIntPropAsNotNullable", "Int!")] - [TestCase(NFS.NonNullTemplates, typeof(Widget), "fixedIntPropAsNotNullable", "Int!")] - [TestCase(NFS.NonNullReferenceTypes, typeof(Widget), "fixedIntPropAsNotNullable", "Int!")] + [TestCase(NullRules.None, typeof(Widget), "fixedIntPropAsNotNullable", "Int!")] + [TestCase(NullRules.NonNullStrings, typeof(Widget), "fixedIntPropAsNotNullable", "Int!")] + [TestCase(NullRules.NonNullLists, typeof(Widget), "fixedIntPropAsNotNullable", "Int!")] + [TestCase(NullRules.NonNullIntermediateTypes, typeof(Widget), "fixedIntPropAsNotNullable", "Int!")] + [TestCase(NullRules.NonNullReferenceTypes, typeof(Widget), "fixedIntPropAsNotNullable", "Int!")] // string type, not fixed, object graph type - [TestCase(NFS.None, typeof(Widget), "stringProp", "String")] - [TestCase(NFS.NonNullStrings, typeof(Widget), "stringProp", "String!")] - [TestCase(NFS.NonNullInputStrings, typeof(Widget), "stringProp", "String")] - [TestCase(NFS.NonNullOutputStrings, typeof(Widget), "stringProp", "String!")] - [TestCase(NFS.NonNullLists, typeof(Widget), "stringProp", "String")] - [TestCase(NFS.NonNullTemplates, typeof(Widget), "stringProp", "String")] - [TestCase(NFS.NonNullReferenceTypes, typeof(Widget), "stringProp", "String")] + [TestCase(NullRules.None, typeof(Widget), "stringProp", "String")] + [TestCase(NullRules.NonNullStrings, typeof(Widget), "stringProp", "String!")] + [TestCase(NullRules.NonNullInputStrings, typeof(Widget), "stringProp", "String")] + [TestCase(NullRules.NonNullOutputStrings, typeof(Widget), "stringProp", "String!")] + [TestCase(NullRules.NonNullLists, typeof(Widget), "stringProp", "String")] + [TestCase(NullRules.NonNullIntermediateTypes, typeof(Widget), "stringProp", "String")] + [TestCase(NullRules.NonNullReferenceTypes, typeof(Widget), "stringProp", "String")] // string, fixed, object graph type - [TestCase(NFS.None, typeof(Widget), "fixedStringProp", "String")] - [TestCase(NFS.NonNullStrings, typeof(Widget), "fixedStringProp", "String")] - [TestCase(NFS.NonNullInputStrings, typeof(Widget), "fixedStringProp", "String")] - [TestCase(NFS.NonNullOutputStrings, typeof(Widget), "fixedStringProp", "String")] - [TestCase(NFS.NonNullLists, typeof(Widget), "fixedStringProp", "String")] - [TestCase(NFS.NonNullTemplates, typeof(Widget), "fixedStringProp", "String")] - [TestCase(NFS.NonNullReferenceTypes, typeof(Widget), "fixedStringProp", "String")] + [TestCase(NullRules.None, typeof(Widget), "fixedStringProp", "String")] + [TestCase(NullRules.NonNullStrings, typeof(Widget), "fixedStringProp", "String")] + [TestCase(NullRules.NonNullInputStrings, typeof(Widget), "fixedStringProp", "String")] + [TestCase(NullRules.NonNullOutputStrings, typeof(Widget), "fixedStringProp", "String")] + [TestCase(NullRules.NonNullLists, typeof(Widget), "fixedStringProp", "String")] + [TestCase(NullRules.NonNullIntermediateTypes, typeof(Widget), "fixedStringProp", "String")] + [TestCase(NullRules.NonNullReferenceTypes, typeof(Widget), "fixedStringProp", "String")] // object reference, not fixed, object graph type - [TestCase(NFS.None, typeof(Widget), "referenceProp", "Widget")] - [TestCase(NFS.NonNullStrings, typeof(Widget), "referenceProp", "Widget")] - [TestCase(NFS.NonNullLists, typeof(Widget), "referenceProp", "Widget")] - [TestCase(NFS.NonNullTemplates, typeof(Widget), "referenceProp", "Widget")] - [TestCase(NFS.NonNullReferenceTypes, typeof(Widget), "referenceProp", "Widget!")] + [TestCase(NullRules.None, typeof(Widget), "referenceProp", "Widget")] + [TestCase(NullRules.NonNullStrings, typeof(Widget), "referenceProp", "Widget")] + [TestCase(NullRules.NonNullLists, typeof(Widget), "referenceProp", "Widget")] + [TestCase(NullRules.NonNullIntermediateTypes, typeof(Widget), "referenceProp", "Widget")] + [TestCase(NullRules.NonNullReferenceTypes, typeof(Widget), "referenceProp", "Widget!")] // object reference, fixed, object graph type - [TestCase(NFS.None, typeof(Widget), "fixedReferenceProp", "Widget")] - [TestCase(NFS.NonNullStrings, typeof(Widget), "fixedReferenceProp", "Widget")] - [TestCase(NFS.NonNullLists, typeof(Widget), "fixedReferenceProp", "Widget")] - [TestCase(NFS.NonNullTemplates, typeof(Widget), "fixedReferenceProp", "Widget")] - [TestCase(NFS.NonNullReferenceTypes, typeof(Widget), "fixedReferenceProp", "Widget")] + [TestCase(NullRules.None, typeof(Widget), "fixedReferenceProp", "Widget")] + [TestCase(NullRules.NonNullStrings, typeof(Widget), "fixedReferenceProp", "Widget")] + [TestCase(NullRules.NonNullLists, typeof(Widget), "fixedReferenceProp", "Widget")] + [TestCase(NullRules.NonNullIntermediateTypes, typeof(Widget), "fixedReferenceProp", "Widget")] + [TestCase(NullRules.NonNullReferenceTypes, typeof(Widget), "fixedReferenceProp", "Widget")] // value type, not fixed, interface graph type - [TestCase(NFS.None, typeof(IWidgetInterface), "intProp", "Int!")] - [TestCase(NFS.NonNullStrings, typeof(IWidgetInterface), "intProp", "Int!")] - [TestCase(NFS.NonNullLists, typeof(IWidgetInterface), "intProp", "Int!")] - [TestCase(NFS.NonNullTemplates, typeof(IWidgetInterface), "intProp", "Int!")] - [TestCase(NFS.NonNullReferenceTypes, typeof(IWidgetInterface), "intProp", "Int!")] + [TestCase(NullRules.None, typeof(IWidgetInterface), "intProp", "Int!")] + [TestCase(NullRules.NonNullStrings, typeof(IWidgetInterface), "intProp", "Int!")] + [TestCase(NullRules.NonNullLists, typeof(IWidgetInterface), "intProp", "Int!")] + [TestCase(NullRules.NonNullIntermediateTypes, typeof(IWidgetInterface), "intProp", "Int!")] + [TestCase(NullRules.NonNullReferenceTypes, typeof(IWidgetInterface), "intProp", "Int!")] // value type, fixed as nullable, interface graph type - [TestCase(NFS.None, typeof(IWidgetInterface), "fixedIntPropAsNullable", "Int")] - [TestCase(NFS.NonNullStrings, typeof(IWidgetInterface), "fixedIntPropAsNullable", "Int")] - [TestCase(NFS.NonNullLists, typeof(IWidgetInterface), "fixedIntPropAsNullable", "Int")] - [TestCase(NFS.NonNullTemplates, typeof(IWidgetInterface), "fixedIntPropAsNullable", "Int")] - [TestCase(NFS.NonNullReferenceTypes, typeof(IWidgetInterface), "fixedIntPropAsNullable", "Int")] + [TestCase(NullRules.None, typeof(IWidgetInterface), "fixedIntPropAsNullable", "Int")] + [TestCase(NullRules.NonNullStrings, typeof(IWidgetInterface), "fixedIntPropAsNullable", "Int")] + [TestCase(NullRules.NonNullLists, typeof(IWidgetInterface), "fixedIntPropAsNullable", "Int")] + [TestCase(NullRules.NonNullIntermediateTypes, typeof(IWidgetInterface), "fixedIntPropAsNullable", "Int")] + [TestCase(NullRules.NonNullReferenceTypes, typeof(IWidgetInterface), "fixedIntPropAsNullable", "Int")] // value type, fixed as not nullable, interface graph type - [TestCase(NFS.None, typeof(IWidgetInterface), "fixedIntPropAsNotNullable", "Int!")] - [TestCase(NFS.NonNullStrings, typeof(IWidgetInterface), "fixedIntPropAsNotNullable", "Int!")] - [TestCase(NFS.NonNullLists, typeof(IWidgetInterface), "fixedIntPropAsNotNullable", "Int!")] - [TestCase(NFS.NonNullTemplates, typeof(IWidgetInterface), "fixedIntPropAsNotNullable", "Int!")] - [TestCase(NFS.NonNullReferenceTypes, typeof(IWidgetInterface), "fixedIntPropAsNotNullable", "Int!")] + [TestCase(NullRules.None, typeof(IWidgetInterface), "fixedIntPropAsNotNullable", "Int!")] + [TestCase(NullRules.NonNullStrings, typeof(IWidgetInterface), "fixedIntPropAsNotNullable", "Int!")] + [TestCase(NullRules.NonNullLists, typeof(IWidgetInterface), "fixedIntPropAsNotNullable", "Int!")] + [TestCase(NullRules.NonNullIntermediateTypes, typeof(IWidgetInterface), "fixedIntPropAsNotNullable", "Int!")] + [TestCase(NullRules.NonNullReferenceTypes, typeof(IWidgetInterface), "fixedIntPropAsNotNullable", "Int!")] // string type, not fixed, interface graph type - [TestCase(NFS.None, typeof(IWidgetInterface), "stringProp", "String")] - [TestCase(NFS.NonNullStrings, typeof(IWidgetInterface), "stringProp", "String!")] - [TestCase(NFS.NonNullInputStrings, typeof(IWidgetInterface), "stringProp", "String")] - [TestCase(NFS.NonNullOutputStrings, typeof(IWidgetInterface), "stringProp", "String!")] - [TestCase(NFS.NonNullLists, typeof(IWidgetInterface), "stringProp", "String")] - [TestCase(NFS.NonNullTemplates, typeof(IWidgetInterface), "stringProp", "String")] - [TestCase(NFS.NonNullReferenceTypes, typeof(IWidgetInterface), "stringProp", "String")] + [TestCase(NullRules.None, typeof(IWidgetInterface), "stringProp", "String")] + [TestCase(NullRules.NonNullStrings, typeof(IWidgetInterface), "stringProp", "String!")] + [TestCase(NullRules.NonNullInputStrings, typeof(IWidgetInterface), "stringProp", "String")] + [TestCase(NullRules.NonNullOutputStrings, typeof(IWidgetInterface), "stringProp", "String!")] + [TestCase(NullRules.NonNullLists, typeof(IWidgetInterface), "stringProp", "String")] + [TestCase(NullRules.NonNullIntermediateTypes, typeof(IWidgetInterface), "stringProp", "String")] + [TestCase(NullRules.NonNullReferenceTypes, typeof(IWidgetInterface), "stringProp", "String")] // string, fixed, interface graph type - [TestCase(NFS.None, typeof(IWidgetInterface), "fixedStringProp", "String")] - [TestCase(NFS.NonNullStrings, typeof(IWidgetInterface), "fixedStringProp", "String")] - [TestCase(NFS.NonNullInputStrings, typeof(IWidgetInterface), "fixedStringProp", "String")] - [TestCase(NFS.NonNullOutputStrings, typeof(IWidgetInterface), "fixedStringProp", "String")] - [TestCase(NFS.NonNullLists, typeof(IWidgetInterface), "fixedStringProp", "String")] - [TestCase(NFS.NonNullTemplates, typeof(IWidgetInterface), "fixedStringProp", "String")] - [TestCase(NFS.NonNullReferenceTypes, typeof(IWidgetInterface), "fixedStringProp", "String")] + [TestCase(NullRules.None, typeof(IWidgetInterface), "fixedStringProp", "String")] + [TestCase(NullRules.NonNullStrings, typeof(IWidgetInterface), "fixedStringProp", "String")] + [TestCase(NullRules.NonNullInputStrings, typeof(IWidgetInterface), "fixedStringProp", "String")] + [TestCase(NullRules.NonNullOutputStrings, typeof(IWidgetInterface), "fixedStringProp", "String")] + [TestCase(NullRules.NonNullLists, typeof(IWidgetInterface), "fixedStringProp", "String")] + [TestCase(NullRules.NonNullIntermediateTypes, typeof(IWidgetInterface), "fixedStringProp", "String")] + [TestCase(NullRules.NonNullReferenceTypes, typeof(IWidgetInterface), "fixedStringProp", "String")] // object reference, not fixed, interface graph type - [TestCase(NFS.None, typeof(IWidgetInterface), "referenceProp", "IWidgetInterface")] - [TestCase(NFS.NonNullStrings, typeof(IWidgetInterface), "referenceProp", "IWidgetInterface")] - [TestCase(NFS.NonNullLists, typeof(IWidgetInterface), "referenceProp", "IWidgetInterface")] - [TestCase(NFS.NonNullTemplates, typeof(IWidgetInterface), "referenceProp", "IWidgetInterface")] - [TestCase(NFS.NonNullReferenceTypes, typeof(IWidgetInterface), "referenceProp", "IWidgetInterface!")] + [TestCase(NullRules.None, typeof(IWidgetInterface), "referenceProp", "IWidgetInterface")] + [TestCase(NullRules.NonNullStrings, typeof(IWidgetInterface), "referenceProp", "IWidgetInterface")] + [TestCase(NullRules.NonNullLists, typeof(IWidgetInterface), "referenceProp", "IWidgetInterface")] + [TestCase(NullRules.NonNullIntermediateTypes, typeof(IWidgetInterface), "referenceProp", "IWidgetInterface")] + [TestCase(NullRules.NonNullReferenceTypes, typeof(IWidgetInterface), "referenceProp", "IWidgetInterface!")] // object reference, fixed, interface graph type - [TestCase(NFS.None, typeof(IWidgetInterface), "fixedReferenceProp", "IWidgetInterface")] - [TestCase(NFS.NonNullStrings, typeof(IWidgetInterface), "fixedReferenceProp", "IWidgetInterface")] - [TestCase(NFS.NonNullLists, typeof(IWidgetInterface), "fixedReferenceProp", "IWidgetInterface")] - [TestCase(NFS.NonNullTemplates, typeof(IWidgetInterface), "fixedReferenceProp", "IWidgetInterface")] - [TestCase(NFS.NonNullReferenceTypes, typeof(IWidgetInterface), "fixedReferenceProp", "IWidgetInterface")] + [TestCase(NullRules.None, typeof(IWidgetInterface), "fixedReferenceProp", "IWidgetInterface")] + [TestCase(NullRules.NonNullStrings, typeof(IWidgetInterface), "fixedReferenceProp", "IWidgetInterface")] + [TestCase(NullRules.NonNullLists, typeof(IWidgetInterface), "fixedReferenceProp", "IWidgetInterface")] + [TestCase(NullRules.NonNullIntermediateTypes, typeof(IWidgetInterface), "fixedReferenceProp", "IWidgetInterface")] + [TestCase(NullRules.NonNullReferenceTypes, typeof(IWidgetInterface), "fixedReferenceProp", "IWidgetInterface")] public void Integration_ConcreteObjectTypes_FieldTypeExpressionTests( - NFS strategy, + NullRules strategy, Type concreteType, string fieldName, string expectedTypeExpression) { - var formatter = new GraphSchemaFormatStrategy(); + var formatter = new SchemaFormatStrategy(); formatter.NullabilityStrategy = strategy; var server = new TestServerBuilder() @@ -150,28 +150,28 @@ public void Integration_ConcreteObjectTypes_FieldTypeExpressionTests( } // top level templated item returning a virtual type - [TestCase(NFS.None, "Query_Widgets", "path1", "Query_Widgets_Path1")] - [TestCase(NFS.NonNullTemplates, "Query_Widgets", "path1", "Query_Widgets_Path1!")] - [TestCase(NFS.NonNullStrings, "Query_Widgets", "path1", "Query_Widgets_Path1")] - [TestCase(NFS.NonNullInputStrings, "Query_Widgets", "path1", "Query_Widgets_Path1")] - [TestCase(NFS.NonNullOutputStrings, "Query_Widgets", "path1", "Query_Widgets_Path1")] - [TestCase(NFS.NonNullLists, "Query_Widgets", "path1", "Query_Widgets_Path1")] - [TestCase(NFS.NonNullReferenceTypes, "Query_Widgets", "path1", "Query_Widgets_Path1")] + [TestCase(NullRules.None, "Query_Widgets", "path1", "Query_Widgets_Path1")] + [TestCase(NullRules.NonNullIntermediateTypes, "Query_Widgets", "path1", "Query_Widgets_Path1!")] + [TestCase(NullRules.NonNullStrings, "Query_Widgets", "path1", "Query_Widgets_Path1")] + [TestCase(NullRules.NonNullInputStrings, "Query_Widgets", "path1", "Query_Widgets_Path1")] + [TestCase(NullRules.NonNullOutputStrings, "Query_Widgets", "path1", "Query_Widgets_Path1")] + [TestCase(NullRules.NonNullLists, "Query_Widgets", "path1", "Query_Widgets_Path1")] + [TestCase(NullRules.NonNullReferenceTypes, "Query_Widgets", "path1", "Query_Widgets_Path1")] // nested templated item returning a virtual type - [TestCase(NFS.None, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] - [TestCase(NFS.NonNullTemplates, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2!")] - [TestCase(NFS.NonNullStrings, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] - [TestCase(NFS.NonNullInputStrings, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] - [TestCase(NFS.NonNullLists, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] - [TestCase(NFS.NonNullReferenceTypes, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] + [TestCase(NullRules.None, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] + [TestCase(NullRules.NonNullIntermediateTypes, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2!")] + [TestCase(NullRules.NonNullStrings, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] + [TestCase(NullRules.NonNullInputStrings, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] + [TestCase(NullRules.NonNullLists, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] + [TestCase(NullRules.NonNullReferenceTypes, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] public void VirtualFields_FieldTypeExpressionNullabilityTests( - NFS strategy, + NullRules strategy, string graphTypeName, string fieldName, string expectedTypeExpression) { - var formatter = new GraphSchemaFormatStrategy(); + var formatter = new SchemaFormatStrategy(); formatter.NullabilityStrategy = strategy; var server = new TestServerBuilder() @@ -188,49 +188,53 @@ public void VirtualFields_FieldTypeExpressionNullabilityTests( Assert.AreEqual(expectedTypeExpression, field.TypeExpression.ToString()); } - [TestCase(NFS.None, "intArgument", "Int!")] - [TestCase(NFS.NonNullTemplates, "intArgument", "Int!")] - [TestCase(NFS.NonNullStrings, "intArgument", "Int!")] - [TestCase(NFS.NonNullInputStrings, "intArgument", "Int!")] - [TestCase(NFS.NonNullOutputStrings, "intArgument", "Int!")] - [TestCase(NFS.NonNullReferenceTypes, "intArgument", "Int!")] - - [TestCase(NFS.None, "intArgumentFixed", "Int!")] - [TestCase(NFS.NonNullTemplates, "intArgumentFixed", "Int!")] - [TestCase(NFS.NonNullStrings, "intArgumentFixed", "Int!")] - [TestCase(NFS.NonNullInputStrings, "intArgumentFixed", "Int!")] - [TestCase(NFS.NonNullOutputStrings, "intArgumentFixed", "Int!")] - [TestCase(NFS.NonNullReferenceTypes, "intArgumentFixed", "Int!")] - - [TestCase(NFS.None, "stringArgument", "String")] - [TestCase(NFS.NonNullTemplates, "stringArgument", "String")] - [TestCase(NFS.NonNullStrings, "stringArgument", "String!")] - [TestCase(NFS.NonNullInputStrings, "stringArgument", "String!")] - [TestCase(NFS.NonNullOutputStrings, "stringArgument", "String")] - [TestCase(NFS.NonNullReferenceTypes, "stringArgument", "String")] - - [TestCase(NFS.None, "stringArgumentFixed", "String")] - [TestCase(NFS.NonNullTemplates, "stringArgumentFixed", "String")] - [TestCase(NFS.NonNullStrings, "stringArgumentFixed", "String")] - [TestCase(NFS.NonNullInputStrings, "stringArgumentFixed", "String")] - [TestCase(NFS.NonNullOutputStrings, "stringArgumentFixed", "String")] - [TestCase(NFS.NonNullReferenceTypes, "stringArgumentFixed", "String")] - - [TestCase(NFS.None, "inputObjectArgument", "Input_Widget")] - [TestCase(NFS.NonNullTemplates, "inputObjectArgument", "Input_Widget")] - [TestCase(NFS.NonNullStrings, "inputObjectArgument", "Input_Widget")] - [TestCase(NFS.NonNullReferenceTypes, "inputObjectArgument", "Input_Widget!")] - - [TestCase(NFS.None, "inputObjectArgumentFixed", "Input_Widget")] - [TestCase(NFS.NonNullTemplates, "inputObjectArgumentFixed", "Input_Widget")] - [TestCase(NFS.NonNullStrings, "inputObjectArgumentFixed", "Input_Widget")] - [TestCase(NFS.NonNullReferenceTypes, "inputObjectArgumentFixed", "Input_Widget")] + [TestCase(NullRules.None, "intArgument", "Int!")] + [TestCase(NullRules.NonNullIntermediateTypes, "intArgument", "Int!")] + [TestCase(NullRules.NonNullStrings, "intArgument", "Int!")] + [TestCase(NullRules.NonNullInputStrings, "intArgument", "Int!")] + [TestCase(NullRules.NonNullOutputStrings, "intArgument", "Int!")] + [TestCase(NullRules.NonNullReferenceTypes, "intArgument", "Int!")] + + [TestCase(NullRules.None, "intArgumentFixed", "Int!")] + [TestCase(NullRules.NonNullIntermediateTypes, "intArgumentFixed", "Int!")] + [TestCase(NullRules.NonNullStrings, "intArgumentFixed", "Int!")] + [TestCase(NullRules.NonNullInputStrings, "intArgumentFixed", "Int!")] + [TestCase(NullRules.NonNullOutputStrings, "intArgumentFixed", "Int!")] + [TestCase(NullRules.NonNullReferenceTypes, "intArgumentFixed", "Int!")] + + [TestCase(NullRules.None, "stringArgument", "String")] + [TestCase(NullRules.NonNullIntermediateTypes, "stringArgument", "String")] + [TestCase(NullRules.NonNullStrings, "stringArgument", "String!")] + [TestCase(NullRules.NonNullInputStrings, "stringArgument", "String!")] + [TestCase(NullRules.NonNullOutputStrings, "stringArgument", "String")] + [TestCase(NullRules.NonNullReferenceTypes, "stringArgument", "String")] + + [TestCase(NullRules.None, "stringArgumentFixed", "String")] + [TestCase(NullRules.NonNullIntermediateTypes, "stringArgumentFixed", "String")] + [TestCase(NullRules.NonNullStrings, "stringArgumentFixed", "String")] + [TestCase(NullRules.NonNullInputStrings, "stringArgumentFixed", "String")] + [TestCase(NullRules.NonNullOutputStrings, "stringArgumentFixed", "String")] + [TestCase(NullRules.NonNullReferenceTypes, "stringArgumentFixed", "String")] + + [TestCase(NullRules.None, "inputObjectArgument", "Input_Widget")] + [TestCase(NullRules.NonNullIntermediateTypes, "inputObjectArgument", "Input_Widget")] + [TestCase(NullRules.NonNullStrings, "inputObjectArgument", "Input_Widget")] + [TestCase(NullRules.NonNullInputStrings, "inputObjectArgument", "Input_Widget")] + [TestCase(NullRules.NonNullOutputStrings, "inputObjectArgument", "Input_Widget")] + [TestCase(NullRules.NonNullReferenceTypes, "inputObjectArgument", "Input_Widget!")] + + [TestCase(NullRules.None, "inputObjectArgumentFixed", "Input_Widget")] + [TestCase(NullRules.NonNullIntermediateTypes, "inputObjectArgumentFixed", "Input_Widget")] + [TestCase(NullRules.NonNullStrings, "inputObjectArgumentFixed", "Input_Widget")] + [TestCase(NullRules.NonNullInputStrings, "inputObjectArgumentFixed", "Input_Widget")] + [TestCase(NullRules.NonNullOutputStrings, "inputObjectArgumentFixed", "Input_Widget")] + [TestCase(NullRules.NonNullReferenceTypes, "inputObjectArgumentFixed", "Input_Widget")] public void Integration_FieldArgumentTypeExpressionNullabilityTests( - NFS strategy, + NullRules strategy, string fieldName, string expectedTypeExpression) { - var formatter = new GraphSchemaFormatStrategy(); + var formatter = new SchemaFormatStrategy(); formatter.NullabilityStrategy = strategy; var server = new TestServerBuilder() @@ -248,44 +252,44 @@ public void Integration_FieldArgumentTypeExpressionNullabilityTests( Assert.AreEqual(expectedTypeExpression, arg1.TypeExpression.ToString()); } - [TestCase(NFS.None, TypeKind.OBJECT, "tripleListProp", "[[[String]]]")] - [TestCase(NFS.NonNullStrings, TypeKind.OBJECT, "tripleListProp", "[[[String!]]]")] - [TestCase(NFS.NonNullInputStrings, TypeKind.OBJECT, "tripleListProp", "[[[String]]]")] - [TestCase(NFS.NonNullOutputStrings, TypeKind.OBJECT, "tripleListProp", "[[[String!]]]")] - [TestCase(NFS.NonNullReferenceTypes, TypeKind.OBJECT, "tripleListProp", "[[[String]]]")] - [TestCase(NFS.NonNullLists, TypeKind.OBJECT, "tripleListProp", "[[[String]!]!]!")] - [TestCase(NFS.NonNullLists | NFS.NonNullStrings, TypeKind.OBJECT, "tripleListProp", "[[[String!]!]!]!")] - - [TestCase(NFS.None, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NFS.NonNullStrings, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NFS.NonNullInputStrings, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NFS.NonNullOutputStrings, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NFS.NonNullLists, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NFS.NonNullReferenceTypes, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NFS.NonNullLists | NFS.NonNullStrings, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] - - [TestCase(NFS.None, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]]]")] - [TestCase(NFS.NonNullStrings, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String!]]]")] - [TestCase(NFS.NonNullInputStrings, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String!]]]")] - [TestCase(NFS.NonNullOutputStrings, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]]]")] - [TestCase(NFS.NonNullReferenceTypes, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]]]")] - [TestCase(NFS.NonNullLists, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]!]!]!")] - [TestCase(NFS.NonNullLists | NFS.NonNullStrings, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String!]!]!]!")] - - [TestCase(NFS.None, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NFS.NonNullStrings, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NFS.NonNullInputStrings, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NFS.NonNullOutputStrings, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NFS.NonNullLists, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NFS.NonNullReferenceTypes, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NFS.NonNullLists | NFS.NonNullStrings, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NullRules.None, TypeKind.OBJECT, "tripleListProp", "[[[String]]]")] + [TestCase(NullRules.NonNullStrings, TypeKind.OBJECT, "tripleListProp", "[[[String!]]]")] + [TestCase(NullRules.NonNullInputStrings, TypeKind.OBJECT, "tripleListProp", "[[[String]]]")] + [TestCase(NullRules.NonNullOutputStrings, TypeKind.OBJECT, "tripleListProp", "[[[String!]]]")] + [TestCase(NullRules.NonNullReferenceTypes, TypeKind.OBJECT, "tripleListProp", "[[[String]]]")] + [TestCase(NullRules.NonNullLists, TypeKind.OBJECT, "tripleListProp", "[[[String]!]!]!")] + [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, TypeKind.OBJECT, "tripleListProp", "[[[String!]!]!]!")] + + [TestCase(NullRules.None, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NullRules.NonNullStrings, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NullRules.NonNullInputStrings, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NullRules.NonNullOutputStrings, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NullRules.NonNullLists, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NullRules.NonNullReferenceTypes, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] + + [TestCase(NullRules.None, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]]]")] + [TestCase(NullRules.NonNullStrings, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String!]]]")] + [TestCase(NullRules.NonNullInputStrings, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String!]]]")] + [TestCase(NullRules.NonNullOutputStrings, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]]]")] + [TestCase(NullRules.NonNullReferenceTypes, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]]]")] + [TestCase(NullRules.NonNullLists, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]!]!]!")] + [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String!]!]!]!")] + + [TestCase(NullRules.None, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NullRules.NonNullStrings, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NullRules.NonNullInputStrings, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NullRules.NonNullOutputStrings, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NullRules.NonNullLists, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NullRules.NonNullReferenceTypes, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] public void Integration_Objects_FieldTypeExpressionListNullabilityTests( - NFS strategy, + NullRules strategy, TypeKind typeKind, string fieldName, string expectedTypeExpression) { - var formatter = new GraphSchemaFormatStrategy(); + var formatter = new SchemaFormatStrategy(); formatter.NullabilityStrategy = strategy; var server = new TestServerBuilder() @@ -310,49 +314,49 @@ public void Integration_Objects_FieldTypeExpressionListNullabilityTests( } } - [TestCase(NFS.None, "intArgument", "[Int!]")] - [TestCase(NFS.NonNullStrings, "intArgument", "[Int!]")] - [TestCase(NFS.NonNullLists, "intArgument", "[Int!]!")] - [TestCase(NFS.NonNullLists | NFS.NonNullStrings, "intArgument", "[Int!]!")] - - [TestCase(NFS.None, "intArgumentFixed", "[Int!]")] - [TestCase(NFS.NonNullStrings, "intArgumentFixed", "[Int!]")] - [TestCase(NFS.NonNullLists, "intArgumentFixed", "[Int!]")] - [TestCase(NFS.NonNullLists | NFS.NonNullStrings, "intArgumentFixed", "[Int!]")] - - [TestCase(NFS.None, "stringArgument", "[String]")] - [TestCase(NFS.NonNullStrings, "stringArgument", "[String!]")] - [TestCase(NFS.NonNullInputStrings, "stringArgument", "[String!]")] - [TestCase(NFS.NonNullOutputStrings, "stringArgument", "[String]")] - [TestCase(NFS.NonNullLists, "stringArgument", "[String]!")] - [TestCase(NFS.NonNullLists | NFS.NonNullStrings, "stringArgument", "[String!]!")] - - [TestCase(NFS.None, "stringArgumentFixed", "[String]")] - [TestCase(NFS.NonNullStrings, "stringArgumentFixed", "[String]")] - [TestCase(NFS.NonNullInputStrings, "stringArgumentFixed", "[String]")] - [TestCase(NFS.NonNullOutputStrings, "stringArgumentFixed", "[String]")] - [TestCase(NFS.NonNullLists, "stringArgumentFixed", "[String]")] - [TestCase(NFS.NonNullLists | NFS.NonNullStrings, "stringArgumentFixed", "[String]")] - - [TestCase(NFS.None, "inputObjectArgument", "[Input_Widget]")] - [TestCase(NFS.NonNullStrings, "inputObjectArgument", "[Input_Widget]")] - [TestCase(NFS.NonNullReferenceTypes, "inputObjectArgument", "[Input_Widget!]")] - [TestCase(NFS.NonNullLists, "inputObjectArgument", "[Input_Widget]!")] - [TestCase(NFS.NonNullLists | NFS.NonNullStrings, "inputObjectArgument", "[Input_Widget]!")] - [TestCase(NFS.NonNullLists | NFS.NonNullReferenceTypes, "inputObjectArgument", "[Input_Widget!]!")] - - [TestCase(NFS.None, "inputObjectArgumentFixed", "[Input_Widget]")] - [TestCase(NFS.NonNullStrings, "inputObjectArgumentFixed", "[Input_Widget]")] - [TestCase(NFS.NonNullReferenceTypes, "inputObjectArgumentFixed", "[Input_Widget]")] - [TestCase(NFS.NonNullLists, "inputObjectArgumentFixed", "[Input_Widget]")] - [TestCase(NFS.NonNullLists | NFS.NonNullStrings, "inputObjectArgumentFixed", "[Input_Widget]")] - [TestCase(NFS.NonNullLists | NFS.NonNullReferenceTypes, "inputObjectArgumentFixed", "[Input_Widget]")] + [TestCase(NullRules.None, "intArgument", "[Int!]")] + [TestCase(NullRules.NonNullStrings, "intArgument", "[Int!]")] + [TestCase(NullRules.NonNullLists, "intArgument", "[Int!]!")] + [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, "intArgument", "[Int!]!")] + + [TestCase(NullRules.None, "intArgumentFixed", "[Int!]")] + [TestCase(NullRules.NonNullStrings, "intArgumentFixed", "[Int!]")] + [TestCase(NullRules.NonNullLists, "intArgumentFixed", "[Int!]")] + [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, "intArgumentFixed", "[Int!]")] + + [TestCase(NullRules.None, "stringArgument", "[String]")] + [TestCase(NullRules.NonNullStrings, "stringArgument", "[String!]")] + [TestCase(NullRules.NonNullInputStrings, "stringArgument", "[String!]")] + [TestCase(NullRules.NonNullOutputStrings, "stringArgument", "[String]")] + [TestCase(NullRules.NonNullLists, "stringArgument", "[String]!")] + [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, "stringArgument", "[String!]!")] + + [TestCase(NullRules.None, "stringArgumentFixed", "[String]")] + [TestCase(NullRules.NonNullStrings, "stringArgumentFixed", "[String]")] + [TestCase(NullRules.NonNullInputStrings, "stringArgumentFixed", "[String]")] + [TestCase(NullRules.NonNullOutputStrings, "stringArgumentFixed", "[String]")] + [TestCase(NullRules.NonNullLists, "stringArgumentFixed", "[String]")] + [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, "stringArgumentFixed", "[String]")] + + [TestCase(NullRules.None, "inputObjectArgument", "[Input_Widget]")] + [TestCase(NullRules.NonNullStrings, "inputObjectArgument", "[Input_Widget]")] + [TestCase(NullRules.NonNullReferenceTypes, "inputObjectArgument", "[Input_Widget!]")] + [TestCase(NullRules.NonNullLists, "inputObjectArgument", "[Input_Widget]!")] + [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, "inputObjectArgument", "[Input_Widget]!")] + [TestCase(NullRules.NonNullLists | NullRules.NonNullReferenceTypes, "inputObjectArgument", "[Input_Widget!]!")] + + [TestCase(NullRules.None, "inputObjectArgumentFixed", "[Input_Widget]")] + [TestCase(NullRules.NonNullStrings, "inputObjectArgumentFixed", "[Input_Widget]")] + [TestCase(NullRules.NonNullReferenceTypes, "inputObjectArgumentFixed", "[Input_Widget]")] + [TestCase(NullRules.NonNullLists, "inputObjectArgumentFixed", "[Input_Widget]")] + [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, "inputObjectArgumentFixed", "[Input_Widget]")] + [TestCase(NullRules.NonNullLists | NullRules.NonNullReferenceTypes, "inputObjectArgumentFixed", "[Input_Widget]")] public void Integration_Controllers_FieldTypeExpressionListNullabilityTests( - NFS strategy, + NullRules strategy, string fieldName, string expectedTypeExpression) { - var formatter = new GraphSchemaFormatStrategy(); + var formatter = new SchemaFormatStrategy(); formatter.NullabilityStrategy = strategy; var server = new TestServerBuilder() @@ -373,8 +377,8 @@ public void Integration_Controllers_FieldTypeExpressionListNullabilityTests( [Test] public void InputObject_NullableField_WithNonNullDefault_DefaultIsRetained_WhenFieldMadeNonNull() { - var formatter = new GraphSchemaFormatStrategy(); - formatter.NullabilityStrategy = NFS.NonNullInputStrings; + var formatter = new SchemaFormatStrategy(); + formatter.NullabilityStrategy = NullRules.NonNullInputStrings; var server = new TestServerBuilder() .AddGraphQL(o => @@ -395,8 +399,8 @@ public void InputObject_NullableField_WithNonNullDefault_DefaultIsRetained_WhenF [Test] public void FieldArgument_WithNonNullDefault_DefaultIsRetained_WhenFieldMadeNonNull() { - var formatter = new GraphSchemaFormatStrategy(); - formatter.NullabilityStrategy = NFS.NonNullInputStrings; + var formatter = new SchemaFormatStrategy(); + formatter.NullabilityStrategy = NullRules.NonNullInputStrings; var server = new TestServerBuilder() .AddGraphQL(o => diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFormatStrategyBuilderTests.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFormatStrategyBuilderTests.cs index 05f92c71c..0ff3feaa0 100644 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFormatStrategyBuilderTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFormatStrategyBuilderTests.cs @@ -18,100 +18,100 @@ public class GraphSchemaFormatStrategyBuilderTests [Test] public void EnsureDefaults() { - var strat = GraphSchemaFormatStrategyBuilder + var strat = (SchemaFormatStrategy)SchemaFormatStrategyBuilder .Create() .Build(); - Assert.AreEqual(GraphNameFormatStrategy.ProperCase, strat.GraphTypeNameStrategy); - Assert.AreEqual(GraphNameFormatStrategy.CamelCase, strat.FieldNameStrategy); - Assert.AreEqual(GraphNameFormatStrategy.UpperCase, strat.EnumValueStrategy); - Assert.AreEqual(NullabilityFormatStrategy.Default, strat.NullabilityStrategy); + Assert.AreEqual(SchemaItemNameFormatOptions.ProperCase, strat.GraphTypeNameStrategy); + Assert.AreEqual(SchemaItemNameFormatOptions.CamelCase, strat.FieldNameStrategy); + Assert.AreEqual(SchemaItemNameFormatOptions.UpperCase, strat.EnumValueStrategy); + Assert.AreEqual(TypeExpressionNullabilityFormatRules.Default, strat.NullabilityStrategy); } [Test] public void WithGraphTypeNameFormat_UpdatesOnlyTypeNameStrategy() { - var strat = GraphSchemaFormatStrategyBuilder + var strat = (SchemaFormatStrategy)SchemaFormatStrategyBuilder .Create() - .WithGraphTypeNameFormat(GraphNameFormatStrategy.NoChanges) + .WithGraphTypeNameFormat(SchemaItemNameFormatOptions.NoChanges) .Build(); - Assert.AreEqual(GraphNameFormatStrategy.NoChanges, strat.GraphTypeNameStrategy); - Assert.AreEqual(GraphNameFormatStrategy.CamelCase, strat.FieldNameStrategy); - Assert.AreEqual(GraphNameFormatStrategy.UpperCase, strat.EnumValueStrategy); + Assert.AreEqual(SchemaItemNameFormatOptions.NoChanges, strat.GraphTypeNameStrategy); + Assert.AreEqual(SchemaItemNameFormatOptions.CamelCase, strat.FieldNameStrategy); + Assert.AreEqual(SchemaItemNameFormatOptions.UpperCase, strat.EnumValueStrategy); } [Test] public void WithFieldNameFormat_UpdatesOnlyFieldNameStrategy() { - var strat = GraphSchemaFormatStrategyBuilder + var strat = (SchemaFormatStrategy)SchemaFormatStrategyBuilder .Create() - .WithFieldNameFormat(GraphNameFormatStrategy.NoChanges) + .WithFieldNameFormat(SchemaItemNameFormatOptions.NoChanges) .Build(); - Assert.AreEqual(GraphNameFormatStrategy.ProperCase, strat.GraphTypeNameStrategy); - Assert.AreEqual(GraphNameFormatStrategy.NoChanges, strat.FieldNameStrategy); - Assert.AreEqual(GraphNameFormatStrategy.UpperCase, strat.EnumValueStrategy); + Assert.AreEqual(SchemaItemNameFormatOptions.ProperCase, strat.GraphTypeNameStrategy); + Assert.AreEqual(SchemaItemNameFormatOptions.NoChanges, strat.FieldNameStrategy); + Assert.AreEqual(SchemaItemNameFormatOptions.UpperCase, strat.EnumValueStrategy); } [Test] public void WithEnumValueFormat_UpdatesOnlyEnumValueStrategy() { - var strat = GraphSchemaFormatStrategyBuilder + var strat = (SchemaFormatStrategy)SchemaFormatStrategyBuilder .Create() - .WithEnumValueFormat(GraphNameFormatStrategy.NoChanges) + .WithEnumValueFormat(SchemaItemNameFormatOptions.NoChanges) .Build(); - Assert.AreEqual(GraphNameFormatStrategy.ProperCase, strat.GraphTypeNameStrategy); - Assert.AreEqual(GraphNameFormatStrategy.CamelCase, strat.FieldNameStrategy); - Assert.AreEqual(GraphNameFormatStrategy.NoChanges, strat.EnumValueStrategy); + Assert.AreEqual(SchemaItemNameFormatOptions.ProperCase, strat.GraphTypeNameStrategy); + Assert.AreEqual(SchemaItemNameFormatOptions.CamelCase, strat.FieldNameStrategy); + Assert.AreEqual(SchemaItemNameFormatOptions.NoChanges, strat.EnumValueStrategy); } [Test] public void ClearNullabilityRules_UpdatesNullabilityStrategy() { - var strat = GraphSchemaFormatStrategyBuilder + var strat = (SchemaFormatStrategy)SchemaFormatStrategyBuilder .Create() .ClearNullabilityRules() .Build(); - Assert.AreEqual(NullabilityFormatStrategy.None, strat.NullabilityStrategy); + Assert.AreEqual(TypeExpressionNullabilityFormatRules.None, strat.NullabilityStrategy); } [Test] public void WithRequiredObjects_UpdatesNullabilityStrategy() { - var strat = GraphSchemaFormatStrategyBuilder + var strat = (SchemaFormatStrategy)SchemaFormatStrategyBuilder .Create() .WithRequiredObjects() .Build(); - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullTemplates)); - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullReferenceTypes)); + Assert.IsTrue(strat.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullIntermediateTypes)); + Assert.IsTrue(strat.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullReferenceTypes)); } [Test] public void WithRequiredStrings_UpdatesNullabilityStrategy() { - var strat = GraphSchemaFormatStrategyBuilder + var strat = (SchemaFormatStrategy)SchemaFormatStrategyBuilder .Create() .WithRequiredStrings() .Build(); - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullTemplates)); - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullStrings)); + Assert.IsTrue(strat.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullIntermediateTypes)); + Assert.IsTrue(strat.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullStrings)); } [Test] public void WithRequiredLists_UpdatesNullabilityStrategy() { - var strat = GraphSchemaFormatStrategyBuilder + var strat = (SchemaFormatStrategy)SchemaFormatStrategyBuilder .Create() .WithRequiredLists() .Build(); - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullTemplates)); - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullLists)); + Assert.IsTrue(strat.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullIntermediateTypes)); + Assert.IsTrue(strat.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullLists)); } } } \ No newline at end of file diff --git a/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/EnumGraphTypeMakerTests.cs b/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/EnumGraphTypeMakerTests.cs index 5b70e5994..aa4aed68c 100644 --- a/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/EnumGraphTypeMakerTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/EnumGraphTypeMakerTests.cs @@ -87,7 +87,7 @@ public void CreateGraphType_ParsesAsExpected() .AddGraphQL(o => { o.DeclarationOptions.SchemaFormatStrategy = - new GraphSchemaFormatStrategy(enumValueStrategy: GraphNameFormatStrategy.NoChanges); + new SchemaFormatStrategy(enumValueStrategy: SchemaItemNameFormatOptions.NoChanges); }) .Build() .Schema; @@ -174,7 +174,7 @@ public void EnumValueIsKeyword_AndFormattingMatchesKeyword_ThrowsException(Type { var schema = new TestServerBuilder().AddGraphQL(o => { - o.DeclarationOptions.SchemaFormatStrategy = new GraphSchemaFormatStrategy(GraphNameFormatStrategy.LowerCase); + o.DeclarationOptions.SchemaFormatStrategy = new SchemaFormatStrategy(SchemaItemNameFormatOptions.LowerCase); }) .Build() .Schema; diff --git a/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/ScalarGraphTypeMakerTests.cs b/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/ScalarGraphTypeMakerTests.cs index 7ddec4397..e8ccfafbc 100644 --- a/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/ScalarGraphTypeMakerTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/ScalarGraphTypeMakerTests.cs @@ -42,36 +42,36 @@ public void NullType_ReturnsNullResult() } // fixed name scalars will never be renamed - [TestCase(typeof(int), GraphNameFormatStrategy.UpperCase, "Int")] - [TestCase(typeof(int), GraphNameFormatStrategy.LowerCase, "Int")] - [TestCase(typeof(int), GraphNameFormatStrategy.ProperCase, "Int")] - [TestCase(typeof(float), GraphNameFormatStrategy.UpperCase, "Float")] - [TestCase(typeof(float), GraphNameFormatStrategy.LowerCase, "Float")] - [TestCase(typeof(float), GraphNameFormatStrategy.ProperCase, "Float")] - [TestCase(typeof(string), GraphNameFormatStrategy.UpperCase, "String")] - [TestCase(typeof(string), GraphNameFormatStrategy.LowerCase, "String")] - [TestCase(typeof(string), GraphNameFormatStrategy.ProperCase, "String")] - [TestCase(typeof(bool), GraphNameFormatStrategy.UpperCase, "Boolean")] - [TestCase(typeof(bool), GraphNameFormatStrategy.LowerCase, "Boolean")] - [TestCase(typeof(bool), GraphNameFormatStrategy.ProperCase, "Boolean")] - [TestCase(typeof(GraphId), GraphNameFormatStrategy.UpperCase, "ID")] - [TestCase(typeof(GraphId), GraphNameFormatStrategy.LowerCase, "ID")] - [TestCase(typeof(GraphId), GraphNameFormatStrategy.ProperCase, "ID")] + [TestCase(typeof(int), SchemaItemNameFormatOptions.UpperCase, "Int")] + [TestCase(typeof(int), SchemaItemNameFormatOptions.LowerCase, "Int")] + [TestCase(typeof(int), SchemaItemNameFormatOptions.ProperCase, "Int")] + [TestCase(typeof(float), SchemaItemNameFormatOptions.UpperCase, "Float")] + [TestCase(typeof(float), SchemaItemNameFormatOptions.LowerCase, "Float")] + [TestCase(typeof(float), SchemaItemNameFormatOptions.ProperCase, "Float")] + [TestCase(typeof(string), SchemaItemNameFormatOptions.UpperCase, "String")] + [TestCase(typeof(string), SchemaItemNameFormatOptions.LowerCase, "String")] + [TestCase(typeof(string), SchemaItemNameFormatOptions.ProperCase, "String")] + [TestCase(typeof(bool), SchemaItemNameFormatOptions.UpperCase, "Boolean")] + [TestCase(typeof(bool), SchemaItemNameFormatOptions.LowerCase, "Boolean")] + [TestCase(typeof(bool), SchemaItemNameFormatOptions.ProperCase, "Boolean")] + [TestCase(typeof(GraphId), SchemaItemNameFormatOptions.UpperCase, "ID")] + [TestCase(typeof(GraphId), SchemaItemNameFormatOptions.LowerCase, "ID")] + [TestCase(typeof(GraphId), SchemaItemNameFormatOptions.ProperCase, "ID")] // non-fixed scalars will rename themselves - [TestCase(typeof(decimal), GraphNameFormatStrategy.UpperCase, "DECIMAL")] - [TestCase(typeof(decimal), GraphNameFormatStrategy.LowerCase, "decimal")] - [TestCase(typeof(decimal), GraphNameFormatStrategy.ProperCase, "Decimal")] - [TestCase(typeof(Uri), GraphNameFormatStrategy.UpperCase, "URI")] - [TestCase(typeof(Uri), GraphNameFormatStrategy.LowerCase, "uri")] - [TestCase(typeof(Uri), GraphNameFormatStrategy.ProperCase, "Uri")] - public void BuiltInScalar_ObeysNamingRulesOfConfig(Type builtInScalarType, GraphNameFormatStrategy strategy, string expectedName) + [TestCase(typeof(decimal), SchemaItemNameFormatOptions.UpperCase, "DECIMAL")] + [TestCase(typeof(decimal), SchemaItemNameFormatOptions.LowerCase, "decimal")] + [TestCase(typeof(decimal), SchemaItemNameFormatOptions.ProperCase, "Decimal")] + [TestCase(typeof(Uri), SchemaItemNameFormatOptions.UpperCase, "URI")] + [TestCase(typeof(Uri), SchemaItemNameFormatOptions.LowerCase, "uri")] + [TestCase(typeof(Uri), SchemaItemNameFormatOptions.ProperCase, "Uri")] + public void BuiltInScalar_ObeysNamingRulesOfConfig(Type builtInScalarType, SchemaItemNameFormatOptions strategy, string expectedName) { var server = new TestServerBuilder() .AddGraphQL(o => { o.DeclarationOptions.SchemaFormatStrategy - = new GraphSchemaFormatStrategy(strategy); + = new SchemaFormatStrategy(strategy); }) .Build(); From d0a2acafaaadad365541db39dfddc7035a9bd437 Mon Sep 17 00:00:00 2001 From: Kevin Carroll Date: Sat, 18 May 2024 10:53:33 -0700 Subject: [PATCH 3/6] WIP, removed the need for an exposed "name formatter" all type name formatting is done within the schema. no more magic strings. --- .../Formatting/NameFormatTarget.cs | 40 ------ .../Formatting/SchemaFormatStrategy.cs | 60 +++------ ...DefaultGraphQLSchemaFactory_Controllers.cs | 121 +++++++----------- .../FieldResolution/FieldDataItem.cs | 23 +++- .../Execution/Response/ResponseWriterBase.cs | 7 +- .../Configuration/ISchemaFormatStrategy.cs | 10 -- .../Configuration/ISchemaItemFormatRule.cs | 29 +++++ .../PackageQueryResultMiddleware.cs | 1 - .../Schemas/TypeSystem/VirtualGraphField.cs | 17 +-- .../TypeSystem/VirtualObjectGraphType.cs | 84 ++++++++++-- src/graphql-aspnet/graphql-aspnet.csproj | 4 + .../GraphSchemaFormatStrategyBuilderTests.cs | 5 +- 12 files changed, 204 insertions(+), 197 deletions(-) delete mode 100644 src/graphql-aspnet/Configuration/Formatting/NameFormatTarget.cs create mode 100644 src/graphql-aspnet/Interfaces/Configuration/ISchemaItemFormatRule.cs diff --git a/src/graphql-aspnet/Configuration/Formatting/NameFormatTarget.cs b/src/graphql-aspnet/Configuration/Formatting/NameFormatTarget.cs deleted file mode 100644 index c636574c6..000000000 --- a/src/graphql-aspnet/Configuration/Formatting/NameFormatTarget.cs +++ /dev/null @@ -1,40 +0,0 @@ -// ************************************************************* -// project: graphql-aspnet -// -- -// repo: https://github.com/graphql-aspnet -// docs: https://graphql-aspnet.github.io -// -- -// License: MIT -// ************************************************************* - -namespace GraphQL.AspNet.Configuration.Formatting -{ - /// - /// The different categories of entities that support - /// distinct name formatting within a schema. - /// - public enum NameFormatCategory - { - /// - /// The string is being formatted as the name of a field, be it a field on an INPUT_OBJECT, INTERFACE - /// or OBJECT type. - /// - Field, - - /// - /// The string is being formatted as the name of a formal graph type in the schema. - /// - GraphType, - - /// - /// The string is being formatted as a single enum value label within an ENUM graph type. - /// - EnumValue, - - /// - /// The string is being formatted as the name of a directive used in a query without the preceeding '@' - /// symbol. - /// - Directive, - } -} diff --git a/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs index e80076ea2..78e53af32 100644 --- a/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs +++ b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs @@ -9,7 +9,6 @@ namespace GraphQL.AspNet.Configuration.Formatting { - using System; using GraphQL.AspNet.Common.Extensions; using GraphQL.AspNet.Interfaces.Configuration; using GraphQL.AspNet.Interfaces.Schema; @@ -86,33 +85,6 @@ public T ApplySchemaItemRules(ISchemaConfiguration configuration, T schemaIte return schemaItem; } - /// - public string FormatSchemaItemName(string name, NameFormatCategory target) - { - switch (target) - { - case NameFormatCategory.GraphType: - - // enforce non-renaming standards. this is not overrideable a child object. - if (!GlobalTypes.CanBeRenamed(name)) - return name; - - return this.FormatGraphTypeName(name); - - case NameFormatCategory.Field: - return this.FormatFieldName(name); - - case NameFormatCategory.EnumValue: - return this.FormatEnumValueName(name); - - case NameFormatCategory.Directive: - return this.FormatDirectiveName(name); - - default: - throw new InvalidOperationException($"Unknown {nameof(NameFormatCategory)} target: '{target}'"); - } - } - /// /// Formats the directive name to be how it will appear in a query, excluding the '@' /// symbol. @@ -151,6 +123,9 @@ protected virtual string FormatEnumValueName(string name) /// System.String. protected virtual string FormatGraphTypeName(string name) { + if (!GlobalTypes.CanBeRenamed(name)) + return name; + return this.FormatName(name, this.GraphTypeNameStrategy); } @@ -178,7 +153,7 @@ protected virtual IUnionGraphType FormatUnionType(ISchemaConfiguration configura /// IDirective. protected virtual IEnumValue FormatEnumValue(ISchemaConfiguration configuration, IEnumValue enumValue) { - var formattedName = this.FormatSchemaItemName(enumValue.Name, NameFormatCategory.EnumValue); + var formattedName = this.FormatEnumValueName(enumValue.Name); return enumValue.Clone(valueName: formattedName); } @@ -193,7 +168,7 @@ protected virtual IEnumValue FormatEnumValue(ISchemaConfiguration configuration, protected virtual IDirective FormatDirective(ISchemaConfiguration configuration, IDirective directive) { // directives are referenced as fields - var formattedName = this.FormatSchemaItemName(directive.Name, NameFormatCategory.Field); + var formattedName = this.FormatFieldName(directive.Name); return (IDirective)directive.Clone(formattedName); } @@ -207,13 +182,18 @@ protected virtual IDirective FormatDirective(ISchemaConfiguration configuration, /// IGraphType. protected virtual IGraphType FormatGraphType(ISchemaConfiguration configuration, IGraphType graphType) { - if (graphType is IScalarGraphType scalarType) + // ensure all path segments of the virtual type are + // named according to the rules of this schema + if (graphType is VirtualObjectGraphType virtualType) { - if (!GlobalTypes.CanBeRenamed(scalarType.Name)) - return graphType; + var newName = VirtualObjectGraphType.MakeSafeTypeNameFromItemPath( + virtualType.ItemPathTemplate, + this.FormatGraphTypeName); + + return virtualType.Clone(newName); } - var formattedName = this.FormatSchemaItemName(graphType.Name, NameFormatCategory.GraphType); + var formattedName = this.FormatGraphTypeName(graphType.Name); return graphType.Clone(formattedName); } @@ -230,9 +210,9 @@ protected virtual IInputGraphField FormatInputGraphField(ISchemaConfiguration co if (!inputGraphField.TypeExpression.IsFixed) inputGraphField = this.ApplyTypeExpressionNullabilityRules(inputGraphField); - var formattedName = this.FormatSchemaItemName(inputGraphField.Name, NameFormatCategory.Field); + var formattedName = this.FormatFieldName(inputGraphField.Name); var typeExpression = inputGraphField.TypeExpression; - typeExpression = typeExpression.Clone(this.FormatSchemaItemName(typeExpression.TypeName, NameFormatCategory.GraphType)); + typeExpression = typeExpression.Clone(this.FormatGraphTypeName(typeExpression.TypeName)); return inputGraphField.Clone(fieldName: formattedName, typeExpression: typeExpression); } @@ -250,9 +230,9 @@ protected virtual IGraphField FormatGraphField(ISchemaConfiguration configuratio if (!graphField.TypeExpression.IsFixed) graphField = this.ApplyTypeExpressionNullabilityRules(graphField); - var formattedName = this.FormatSchemaItemName(graphField.Name, NameFormatCategory.Field); + var formattedName = this.FormatFieldName(graphField.Name); var typeExpression = graphField.TypeExpression; - typeExpression = typeExpression.Clone(this.FormatSchemaItemName(typeExpression.TypeName, NameFormatCategory.GraphType)); + typeExpression = typeExpression.Clone(this.FormatGraphTypeName(typeExpression.TypeName)); return graphField.Clone(fieldName: formattedName, typeExpression: typeExpression); } @@ -270,9 +250,9 @@ protected virtual IGraphArgument FormatArgument(ISchemaConfiguration configurati if (!argument.TypeExpression.IsFixed) argument = this.ApplyTypeExpressionNullabilityRules(argument); - var formattedName = this.FormatSchemaItemName(argument.Name, NameFormatCategory.Field); + var formattedName = this.FormatFieldName(argument.Name); var typeExpression = argument.TypeExpression; - typeExpression = typeExpression.Clone(this.FormatSchemaItemName(typeExpression.TypeName, NameFormatCategory.GraphType)); + typeExpression = typeExpression.Clone(this.FormatGraphTypeName(typeExpression.TypeName)); return argument.Clone(argumentName: formattedName, typeExpression: typeExpression); } diff --git a/src/graphql-aspnet/Engine/DefaultGraphQLSchemaFactory_Controllers.cs b/src/graphql-aspnet/Engine/DefaultGraphQLSchemaFactory_Controllers.cs index 4710310c5..1be393b36 100644 --- a/src/graphql-aspnet/Engine/DefaultGraphQLSchemaFactory_Controllers.cs +++ b/src/graphql-aspnet/Engine/DefaultGraphQLSchemaFactory_Controllers.cs @@ -10,13 +10,12 @@ namespace GraphQL.AspNet.Engine { using System; - using System.Collections.Generic; using GraphQL.AspNet.Common; - using GraphQL.AspNet.Configuration.Formatting; using GraphQL.AspNet.Execution; using GraphQL.AspNet.Execution.Exceptions; using GraphQL.AspNet.Interfaces.Internal; using GraphQL.AspNet.Interfaces.Schema; + using GraphQL.AspNet.Schemas; using GraphQL.AspNet.Schemas.Generation.TypeTemplates; using GraphQL.AspNet.Schemas.Structural; using GraphQL.AspNet.Schemas.TypeSystem; @@ -103,16 +102,23 @@ protected virtual IObjectGraphType AddOrRetrieveVirtualTypeOwner(IGraphFieldTemp for (var i = 0; i < pathSegments.Count; i++) { var segment = pathSegments[i]; - var formattedName = this.Schema - .Configuration - .DeclarationOptions - .SchemaFormatStrategy - .FormatSchemaItemName(segment.Name, NameFormatCategory.Field); - if (parentType.Fields.ContainsKey(formattedName)) + (var virtualField, var virtualFieldGaphType) = this.CreateVirtualField( + parentType, + segment.Name, + segment, + i == 0 ? action.Parent : null); + + // its possible this field and its graph type already exist because its + // been referenced in path segments elsewhere on this controller. + // If this is the case reference the already added version on the schema + // then perform checks to ensure that the value in the schema matches expectations + // for this newly created virtual field. + // Once verified just use the existing field and continue down the tree. + if (parentType.Fields.ContainsKey(virtualField.Name)) { - var field = parentType[formattedName]; - var foundType = Schema.KnownTypes.FindGraphType(field.TypeExpression.TypeName); + var existingChildField = parentType[virtualField.Name]; + var foundType = Schema.KnownTypes.FindGraphType(existingChildField.TypeExpression.TypeName); var ogt = foundType as IObjectGraphType; if (ogt != null) @@ -125,7 +131,7 @@ protected virtual IObjectGraphType AddOrRetrieveVirtualTypeOwner(IGraphFieldTemp throw new GraphTypeDeclarationException( $"The action '{action.ItemPath}' attempted to nest itself under the {foundType.Kind} graph type '{foundType.Name}', which is returned by " + - $"the path '{field.ItemPath}'. Actions can only be added to virtual graph types created by their parent controller."); + $"the path '{existingChildField.ItemPath}'. Actions can only be added to virtual graph types created by their parent controller."); } if (foundType != null) @@ -137,40 +143,53 @@ protected virtual IObjectGraphType AddOrRetrieveVirtualTypeOwner(IGraphFieldTemp else { throw new GraphTypeDeclarationException( - $"The action '{action.ItemPath.Path}' attempted to nest itself under the field '{field.ItemPath}' but no graph type was found " + + $"The action '{action.ItemPath.Path}' attempted to nest itself under the field '{existingChildField.ItemPath}' but no graph type was found " + "that matches its type."); } } - parentType = this.CreateVirtualFieldOnParent( - parentType, - formattedName, - segment, - i == 0 ? action.Parent : null); + // field doesn't already exist on the schema + // add it and continue down the tree + parentType.Extend(virtualField); + this.Schema.KnownTypes.EnsureGraphType(virtualFieldGaphType); + this.EnsureDependents(virtualField); + parentType = virtualFieldGaphType; } return parentType; } /// - /// Performs an out-of-band append of a new graph field to a parent and - /// returns the virtual type the would returns. + /// Generates a fully qualified field and an associated graph type that can be added to + /// the schema to represent a segment in a field path definition added by the developer. /// /// the parent type to add the new field to. /// Name of the field. - /// The path segment to represent the new field. + /// The path segment to represent the new field. /// The definition item from which graph attributes should be used, if any. Attributes will be set to an empty string if not supplied. - /// The type associated with the field added to the parent type. - protected virtual IObjectGraphType CreateVirtualFieldOnParent( + /// A reference to the new field and the graph type created to represent the fields returned value. + protected virtual (VirtualGraphField VirtualField, IObjectGraphType VirtualFieldGraphType) CreateVirtualField( IObjectGraphType parentType, string fieldName, - ItemPath path, + ItemPath pathTemplate, ISchemaItemTemplate definition = null) { + var returnedGraphType = VirtualObjectGraphType.FromControllerFieldPathTemplate(pathTemplate); + + returnedGraphType = this.Schema + .Configuration + .DeclarationOptions + .SchemaFormatStrategy? + .ApplySchemaItemRules( + this.Schema.Configuration, + returnedGraphType) ?? returnedGraphType; + + var typeExpression = GraphTypeExpression.FromDeclaration(returnedGraphType.Name); + var childField = new VirtualGraphField( fieldName, - path, - this.MakeSafeTypeNameFromItemPath(path)) + pathTemplate, + typeExpression) { IsDepreciated = false, DepreciationReason = string.Empty, @@ -187,57 +206,7 @@ protected virtual IObjectGraphType CreateVirtualFieldOnParent( this.Schema.Configuration, childField) ?? childField; - parentType.Extend(childField); - - // ensure the new graph type that this virtual field will - // return is part of the schema - var graphType = childField.AssociatedGraphType; - graphType = this.Schema - .Configuration - .DeclarationOptions - .SchemaFormatStrategy? - .ApplySchemaItemRules( - this.Schema.Configuration, - graphType) ?? graphType; - - this.Schema.KnownTypes.EnsureGraphType(graphType); - this.EnsureDependents(childField); - return graphType; - } - - /// - /// Makes the unique route being used for this virtual field type safe, removing special control characters - /// but retaining its uniqueness. - /// - /// The path. - /// System.String. - protected virtual string MakeSafeTypeNameFromItemPath(ItemPath path) - { - var segments = new List(); - foreach (var pathSegmentName in path) - { - switch (pathSegmentName) - { - case Constants.Routing.QUERY_ROOT: - segments.Add(Constants.ReservedNames.QUERY_TYPE_NAME); - break; - - case Constants.Routing.MUTATION_ROOT: - segments.Add(Constants.ReservedNames.MUTATION_TYPE_NAME); - break; - - case Constants.Routing.SUBSCRIPTION_ROOT: - segments.Add(Constants.ReservedNames.SUBSCRIPTION_TYPE_NAME); - break; - - default: - segments.Add(this.Schema.Configuration.DeclarationOptions.SchemaFormatStrategy.FormatSchemaItemName(pathSegmentName, NameFormatCategory.GraphType)); - break; - } - } - - segments.Reverse(); - return string.Join("_", segments); + return (childField, returnedGraphType); } /// diff --git a/src/graphql-aspnet/Execution/FieldResolution/FieldDataItem.cs b/src/graphql-aspnet/Execution/FieldResolution/FieldDataItem.cs index f14c8f8d2..d235d89dc 100644 --- a/src/graphql-aspnet/Execution/FieldResolution/FieldDataItem.cs +++ b/src/graphql-aspnet/Execution/FieldResolution/FieldDataItem.cs @@ -17,9 +17,9 @@ namespace GraphQL.AspNet.Execution.FieldResolution using GraphQL.AspNet.Common; using GraphQL.AspNet.Common.Extensions; using GraphQL.AspNet.Execution.Exceptions; - using GraphQL.AspNet.Interfaces.Execution; - using GraphQL.AspNet.Execution.Source; using GraphQL.AspNet.Execution.Response; + using GraphQL.AspNet.Execution.Source; + using GraphQL.AspNet.Interfaces.Execution; using GraphQL.AspNet.Interfaces.Execution.Response; using GraphQL.AspNet.Interfaces.Schema; using GraphQL.AspNet.Schemas; @@ -417,7 +417,24 @@ private bool GenerateSingleValueResult(out IQueryResponseItem result) { var type = resultData.GetType(); var graphType = this.Schema.KnownTypes.FindGraphType(type); - if (graphType is IScalarGraphType sgt) + + if (graphType is IEnumGraphType egt) + { + // convert the enum to its appropriate label + // from the graph type + var enumValue = egt.Values.FindByEnumValue(resultData); + if (enumValue == null) + { + throw new GraphExecutionException( + $"The data value of '{resultData}' " + + $"was expected to be of enum graph type '{egt.Name}' but could not be " + + $"found as a valid enum value registered with the schema.", + this.FieldContext.FieldDocumentPart.Origin); + } + + resultData = enumValue.Name; + } + else if (graphType is IScalarGraphType sgt) { resultData = sgt.Serialize(resultData); } diff --git a/src/graphql-aspnet/Execution/Response/ResponseWriterBase.cs b/src/graphql-aspnet/Execution/Response/ResponseWriterBase.cs index da92076ed..002336e50 100644 --- a/src/graphql-aspnet/Execution/Response/ResponseWriterBase.cs +++ b/src/graphql-aspnet/Execution/Response/ResponseWriterBase.cs @@ -14,7 +14,6 @@ namespace GraphQL.AspNet.Execution.Response using System.Text.Json; using GraphQL.AspNet.Common; using GraphQL.AspNet.Common.Extensions; - using GraphQL.AspNet.Configuration.Formatting; using GraphQL.AspNet.Execution.Source; using GraphQL.AspNet.Interfaces.Configuration; using GraphQL.AspNet.Interfaces.Execution; @@ -98,7 +97,7 @@ protected virtual void WriteMessage(Utf8JsonWriter writer, IGraphMessage message var timestamp = this.TimeLocalizer?.Invoke(message.TimeStamp) ?? message.TimeStamp; this.WriteLeaf(writer, "timestamp", timestamp); - this.WriteLeaf(writer, "severity", message.Severity); + this.WriteLeaf(writer, "severity", message.Severity.ToString().ToUpperInvariant()); if (message.MetaData != null && message.MetaData.Count > 0) { @@ -249,7 +248,7 @@ protected virtual void WriteLeafValue(Utf8JsonWriter writer, object value, bool } if (value.GetType().IsEnum) - value = this.Formatter.FormatSchemaItemName(value.ToString(), NameFormatCategory.EnumValue); + value = value.ToString(); switch (value) { @@ -313,7 +312,6 @@ protected virtual void WriteLeafValue(Utf8JsonWriter writer, object value, bool writer.WriteNumberValue(ush); break; -#if NET6_0_OR_GREATER case DateOnly dateOnly: this.WritePreEncodedStringValue(writer, dateOnly.ToRfc3339String()); break; @@ -321,7 +319,6 @@ protected virtual void WriteLeafValue(Utf8JsonWriter writer, object value, bool case TimeOnly timeOnly: this.WritePreEncodedStringValue(writer, timeOnly.ToRfc3339String()); break; -#endif default: if (convertUnsupportedToString) diff --git a/src/graphql-aspnet/Interfaces/Configuration/ISchemaFormatStrategy.cs b/src/graphql-aspnet/Interfaces/Configuration/ISchemaFormatStrategy.cs index 4b14d6297..fb85dfc53 100644 --- a/src/graphql-aspnet/Interfaces/Configuration/ISchemaFormatStrategy.cs +++ b/src/graphql-aspnet/Interfaces/Configuration/ISchemaFormatStrategy.cs @@ -9,7 +9,6 @@ namespace GraphQL.AspNet.Interfaces.Configuration { - using GraphQL.AspNet.Configuration.Formatting; using GraphQL.AspNet.Interfaces.Schema; /// @@ -46,14 +45,5 @@ public interface ISchemaFormatStrategy /// The updated or altered schema item instance that was formatted. T ApplySchemaItemRules(ISchemaConfiguration configuration, T schemaItem) where T : ISchemaItem; - - /// - /// Formats the provided string as if it were the name of an entity - /// according to the rules of the schema. - /// - /// The name to format. - /// The target schema item entity type. - /// A formatted string. - string FormatSchemaItemName(string name, NameFormatCategory target); } } diff --git a/src/graphql-aspnet/Interfaces/Configuration/ISchemaItemFormatRule.cs b/src/graphql-aspnet/Interfaces/Configuration/ISchemaItemFormatRule.cs new file mode 100644 index 000000000..a9a90eeb5 --- /dev/null +++ b/src/graphql-aspnet/Interfaces/Configuration/ISchemaItemFormatRule.cs @@ -0,0 +1,29 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Interfaces.Configuration +{ + using GraphQL.AspNet.Interfaces.Schema; + + /// + /// A rule that defines a specific set of business logic that can be executed against + /// a schema item just before its inserted intoa schema. + /// + public interface ISchemaItemFormatRule + { + /// + /// Executes this rule against the target schema item + /// + /// The actual type of the schema item being processed. + /// The schema item. + /// The original schema item or a modified version of the schema item. + public T Execute(T schemaItem) + where T : ISchemaItem; + } +} diff --git a/src/graphql-aspnet/Middleware/QueryExecution/Components/PackageQueryResultMiddleware.cs b/src/graphql-aspnet/Middleware/QueryExecution/Components/PackageQueryResultMiddleware.cs index 3e6bc7159..976b79852 100644 --- a/src/graphql-aspnet/Middleware/QueryExecution/Components/PackageQueryResultMiddleware.cs +++ b/src/graphql-aspnet/Middleware/QueryExecution/Components/PackageQueryResultMiddleware.cs @@ -9,7 +9,6 @@ namespace GraphQL.AspNet.Middleware.QueryExecution.Components { - using System; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/graphql-aspnet/Schemas/TypeSystem/VirtualGraphField.cs b/src/graphql-aspnet/Schemas/TypeSystem/VirtualGraphField.cs index 00e33e975..4b80e8a7a 100644 --- a/src/graphql-aspnet/Schemas/TypeSystem/VirtualGraphField.cs +++ b/src/graphql-aspnet/Schemas/TypeSystem/VirtualGraphField.cs @@ -47,20 +47,18 @@ static VirtualGraphField() /// /// Name of the field in the object graph. /// The path segment that represents this virtual field. - /// The type name to use for the virtual type that owns this field. + /// The type expression declared for this field. public VirtualGraphField( string fieldName, ItemPath itemPath, - string parentTypeName) + GraphTypeExpression typeExpression) { Validation.ThrowIfNull(itemPath, nameof(itemPath)); - parentTypeName = Validation.ThrowIfNullWhiteSpaceOrReturn(parentTypeName, nameof(parentTypeName)); this.Name = Validation.ThrowIfNullWhiteSpaceOrReturn(fieldName, nameof(fieldName)); this.ItemPath = Validation.ThrowIfNullOrReturn(itemPath, nameof(itemPath)); - this.AssociatedGraphType = new VirtualObjectGraphType(parentTypeName); - this.TypeExpression = new GraphTypeExpression(parentTypeName); + this.TypeExpression = Validation.ThrowIfNullOrReturn(typeExpression, nameof(typeExpression)); this.Arguments = new GraphFieldArgumentCollection(this); this.Resolver = new GraphControllerVirtualFieldResolver(new VirtualResolvedObject(this.TypeExpression.TypeName)); this.InternalName = $"VirtualField_{this.Name}"; @@ -99,7 +97,7 @@ public IGraphField Clone( var clonedItem = new VirtualGraphField( fieldName, itemPath, - typeExpression.TypeName); + typeExpression); clonedItem.Description = this.Description; clonedItem.TypeExpression = typeExpression.Clone(); @@ -130,13 +128,6 @@ public virtual bool CanResolveForGraphType(IGraphType graphType) return false; } - /// - /// Gets the tracked copy of the graph type that this virtual field will - /// always return. - /// - /// The object graph type this virutal field will return when resolved. - public IObjectGraphType AssociatedGraphType { get; } - /// public IGraphFieldResolver Resolver { get; } diff --git a/src/graphql-aspnet/Schemas/TypeSystem/VirtualObjectGraphType.cs b/src/graphql-aspnet/Schemas/TypeSystem/VirtualObjectGraphType.cs index 653d6dafd..da25143b3 100644 --- a/src/graphql-aspnet/Schemas/TypeSystem/VirtualObjectGraphType.cs +++ b/src/graphql-aspnet/Schemas/TypeSystem/VirtualObjectGraphType.cs @@ -10,9 +10,10 @@ namespace GraphQL.AspNet.Schemas.TypeSystem { using System; + using System.Collections.Generic; using System.Diagnostics; using GraphQL.AspNet.Attributes; - using GraphQL.AspNet.Common.Extensions; + using GraphQL.AspNet.Common; using GraphQL.AspNet.Execution; using GraphQL.AspNet.Interfaces.Schema; using GraphQL.AspNet.Schemas.Structural; @@ -33,18 +34,32 @@ public class VirtualObjectGraphType : ObjectGraphTypeBase, IObjectGraphType, IIn // to allow for proper navigation of an object structure in graphql. This object is generated dynamically from the parsed // metadata of a controller. + /// + /// Constructs a virtual type from the path template extracted from a controller action method. + /// + /// The path template base this virtual type off of. + /// VirtualObjectGraphType. + public static VirtualObjectGraphType FromControllerFieldPathTemplate(ItemPath pathTemplate) + { + var tempName = MakeSafeTypeNameFromItemPath(pathTemplate); + return new VirtualObjectGraphType(tempName, pathTemplate); + } + /// /// Initializes a new instance of the class. /// - /// The name to assign to this type. - public VirtualObjectGraphType(string name) + /// The formal name to assign to the type. + /// The path template that generated this virutal graph type. + private VirtualObjectGraphType(string typeName, ItemPath pathTemplate) : base( - name, - $"{nameof(VirtualObjectGraphType)}_{name}", - new ItemPath(ItemPathRoots.Types, name)) + typeName, + $"{nameof(VirtualObjectGraphType)}_{typeName}", + new ItemPath(ItemPathRoots.Types, typeName)) { + this.ItemPathTemplate = Validation.ThrowIfNullOrReturn(pathTemplate, nameof(pathTemplate)); + // add the __typename as a field for this virtual object - this.Extend(new Introspection_TypeNameMetaField(name)); + this.Extend(new Introspection_TypeNameMetaField(typeName)); } /// @@ -57,13 +72,66 @@ public override bool ValidateObject(object item) public override IGraphType Clone(string typeName = null) { typeName = typeName?.Trim() ?? this.Name; - return new VirtualObjectGraphType(typeName); + return new VirtualObjectGraphType( + typeName, + this.ItemPathTemplate); } /// public override bool IsVirtual => true; + /// + /// Gets the raw path template that was used to define this virtual type. + /// + /// The item path template. + public ItemPath ItemPathTemplate { get; private set; } + /// public Type ObjectType => typeof(VirtualObjectGraphType); + + /// + /// Converts a route path into a unique graph type, removing special control characters + /// but retaining its uniqueness. + /// + /// The path to convert. + /// An optional formatter that will apply + /// special casing to a path segment before its added to the name. + /// System.String. + public static string MakeSafeTypeNameFromItemPath( + ItemPath path, + Func segmentNameFormatter = null) + { + Validation.ThrowIfNull(path, nameof(path)); + + var segments = new List(); + foreach (var pathSegmentName in path) + { + switch (pathSegmentName) + { + case Constants.Routing.QUERY_ROOT: + segments.Add(Constants.ReservedNames.QUERY_TYPE_NAME); + break; + + case Constants.Routing.MUTATION_ROOT: + segments.Add(Constants.ReservedNames.MUTATION_TYPE_NAME); + break; + + case Constants.Routing.SUBSCRIPTION_ROOT: + segments.Add(Constants.ReservedNames.SUBSCRIPTION_TYPE_NAME); + break; + + default: + var segmentName = pathSegmentName; + if (segmentNameFormatter != null) + segmentName = segmentNameFormatter(pathSegmentName); + + segments.Add(segmentName); + break; + } + } + + segments.Reverse(); + return string.Join("_", segments); + } } } \ No newline at end of file diff --git a/src/graphql-aspnet/graphql-aspnet.csproj b/src/graphql-aspnet/graphql-aspnet.csproj index f8b4e8493..8eeb495d4 100644 --- a/src/graphql-aspnet/graphql-aspnet.csproj +++ b/src/graphql-aspnet/graphql-aspnet.csproj @@ -18,4 +18,8 @@ + + + + \ No newline at end of file diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFormatStrategyBuilderTests.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFormatStrategyBuilderTests.cs index 0ff3feaa0..848260965 100644 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFormatStrategyBuilderTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFormatStrategyBuilderTests.cs @@ -114,4 +114,7 @@ public void WithRequiredLists_UpdatesNullabilityStrategy() Assert.IsTrue(strat.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullLists)); } } -} \ No newline at end of file +} + +// Change everything on the formatters over to rules +// figure out how to get rid of the name formatter (probably not possible) \ No newline at end of file From 3a2cd51f34a6271d138aa89e1e9e964de808cbe4 Mon Sep 17 00:00:00 2001 From: Kevin Carroll Date: Sun, 19 May 2024 13:46:31 -0700 Subject: [PATCH 4/6] WIP, revamped nullability settings and name formatters to be "rule based" --- .../Naming/ApplyArgumentNameFormatRule.cs | 44 ++ ...plyArgumentTypeExpressionNameFormatRule.cs | 49 ++ .../Naming/ApplyDirectiveNameFormatRule.cs | 46 ++ .../Naming/ApplyEnumNameFormatRule.cs | 44 ++ .../Naming/ApplyFieldNameFormatRule.cs | 44 ++ .../ApplyFieldTypeExpressionNameFormatRule.cs | 49 ++ .../Naming/ApplyGraphTypeNameFormatRule.cs | 63 +++ ...aphTypeNameFormatToUnionTypeMembersRule.cs | 45 ++ .../Naming/ApplyInputFieldNameFormatRule.cs | 44 ++ ...yInputFieldTypeExpressionNameFormatRule.cs | 49 ++ .../FormatRules/Naming/NameFormatRuleBase.cs | 68 +++ ...mentNonNullItemTypeExpressionFormatRule.cs | 59 +++ ...mentNonNullListTypeExpressionFormatRule.cs | 59 +++ ...ieldNonNullItemTypeExpressionFormatRule.cs | 49 ++ ...ieldNonNullListTypeExpressionFormatRule.cs | 48 ++ ...ieldNonNullItemTypeExpressionFormatRule.cs | 60 +++ ...ieldNonNullListTypeExpressionFormatRule.cs | 59 +++ .../Nullability/NullabilityFormatRuleBase.cs | 47 ++ .../Formatting/SchemaFormatStrategy.cs | 442 ++---------------- .../Formatting/SchemaFormatStrategyBuilder.cs | 259 +++++++--- .../Formatting/SchemaItemNameFormatOptions.cs | 21 +- .../TypeExpressionNullabilityFormatRules.cs | 72 --- .../SchemaDeclarationConfiguration.cs | 4 +- .../Directives/Global/IncludeDirective.cs | 2 +- .../Directives/Global/SkipDirective.cs | 2 +- .../Configuration/ISchemaItemFormatRule.cs | 6 +- .../GraphDirectiveMethodTemplate.cs | 2 - .../Introspection/Model/IntrospectedType.cs | 1 - .../Schemas/TypeSystem/VirtualGraphField.cs | 2 + src/graphql-aspnet/graphql-aspnet.csproj | 4 - .../GraphQLSchemaHelperMethods.cs | 4 +- .../TestServerBuilder{TSchema}.cs | 2 +- .../ConfigurationFieldNamingTests.cs | 8 +- .../{IWidgetInterface.cs => IWidget.cs} | 12 +- .../FormatStrategyTestData/IWidgetList.cs | 28 ++ .../FormatStrategyTestData/Widget.cs | 12 + .../WidgetArgumentController.cs | 30 +- .../FormatStrategyTestData/WidgetDirective.cs | 26 ++ .../FormatStrategyTestData/WidgetList.cs | 14 +- .../WidgetListController.cs | 30 +- .../FormatStrategyTestData/WidgetType.cs | 16 + .../GraphSchemaFomatStrategyTests.cs | 422 ----------------- .../GraphSchemaFormatStrategyBuilderTests.cs | 120 ----- .../SchemaFormatStrategy_NameFormatTests.cs | 223 +++++++++ .../SchemaFormatStrategy_NonNullListTests.cs | 139 ++++++ .../SchemaFormatStrategy_NonNullValueTests.cs | 186 ++++++++ .../Execution/ApolloTracingTests.cs | 14 +- .../TypeMakers/EnumGraphTypeMakerTests.cs | 4 +- .../TypeMakers/ScalarGraphTypeMakerTests.cs | 46 +- 49 files changed, 1920 insertions(+), 1159 deletions(-) create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyArgumentNameFormatRule.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyArgumentTypeExpressionNameFormatRule.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyDirectiveNameFormatRule.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyEnumNameFormatRule.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyFieldNameFormatRule.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyFieldTypeExpressionNameFormatRule.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyGraphTypeNameFormatRule.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyGraphTypeNameFormatToUnionTypeMembersRule.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyInputFieldNameFormatRule.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyInputFieldTypeExpressionNameFormatRule.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/NameFormatRuleBase.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyArgumentNonNullItemTypeExpressionFormatRule.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyArgumentNonNullListTypeExpressionFormatRule.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyFieldNonNullItemTypeExpressionFormatRule.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyFieldNonNullListTypeExpressionFormatRule.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyInputFieldNonNullItemTypeExpressionFormatRule.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyInputFieldNonNullListTypeExpressionFormatRule.cs create mode 100644 src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/NullabilityFormatRuleBase.cs delete mode 100644 src/graphql-aspnet/Configuration/Formatting/TypeExpressionNullabilityFormatRules.cs rename src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/{IWidgetInterface.cs => IWidget.cs} (73%) create mode 100644 src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/IWidgetList.cs create mode 100644 src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetDirective.cs create mode 100644 src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetType.cs delete mode 100644 src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFomatStrategyTests.cs delete mode 100644 src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFormatStrategyBuilderTests.cs create mode 100644 src/unit-tests/graphql-aspnet-tests/Configuration/SchemaFormatStrategy_NameFormatTests.cs create mode 100644 src/unit-tests/graphql-aspnet-tests/Configuration/SchemaFormatStrategy_NonNullListTests.cs create mode 100644 src/unit-tests/graphql-aspnet-tests/Configuration/SchemaFormatStrategy_NonNullValueTests.cs diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyArgumentNameFormatRule.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyArgumentNameFormatRule.cs new file mode 100644 index 000000000..10b9db24e --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyArgumentNameFormatRule.cs @@ -0,0 +1,44 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + + /// + /// Applies a given format pattern to field argument names for the target schema. + /// + public class ApplyArgumentNameFormatRule : NameFormatRuleBase, ISchemaItemFormatRule + { + private readonly TextFormatOptions _formatOption; + + /// + /// Initializes a new instance of the class. + /// + /// The format option. + public ApplyArgumentNameFormatRule(TextFormatOptions formatOption) + { + _formatOption = formatOption; + } + + /// + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem + { + if (schemaItem is IGraphArgument argument) + { + var formattedName = this.FormatText(argument.Name, _formatOption); + schemaItem = (TSchemaItemType)argument.Clone(argumentName: formattedName); + } + + return schemaItem; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyArgumentTypeExpressionNameFormatRule.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyArgumentTypeExpressionNameFormatRule.cs new file mode 100644 index 000000000..20deaaf05 --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyArgumentTypeExpressionNameFormatRule.cs @@ -0,0 +1,49 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + + /// + /// Applies a graph type name format pattern to the type expression on each argument encounterd + /// on fields in OBJECT and INTERFACE types as well as those defined on DIRECTIVE types for the target schema. + /// + internal class ApplyArgumentTypeExpressionNameFormatRule : NameFormatRuleBase, ISchemaItemFormatRule + { + private readonly TextFormatOptions _formatOption; + + /// + /// Initializes a new instance of the class. + /// + /// The format to use for the type name + /// referneced in the argument's type expression. + public ApplyArgumentTypeExpressionNameFormatRule(TextFormatOptions typeExpressionGraphTypeFormat) + { + _formatOption = typeExpressionGraphTypeFormat; + } + + /// + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem + { + if (schemaItem is IGraphArgument argument) + { + var typeExpression = argument.TypeExpression; + var formattedName = this.FormatGraphTypeName(typeExpression.TypeName, _formatOption); + typeExpression = typeExpression.Clone(formattedName); + + schemaItem = (TSchemaItemType)argument.Clone(typeExpression: typeExpression); + } + + return schemaItem; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyDirectiveNameFormatRule.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyDirectiveNameFormatRule.cs new file mode 100644 index 000000000..1a1092ab1 --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyDirectiveNameFormatRule.cs @@ -0,0 +1,46 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + + /// + /// Applies a given format pattern to formal name for directives on the target schema. + /// + internal class ApplyDirectiveNameFormatRule : NameFormatRuleBase, ISchemaItemFormatRule + { + private readonly TextFormatOptions _formatOption; + + /// + /// Initializes a new instance of the class. + /// + /// The format option. + public ApplyDirectiveNameFormatRule(TextFormatOptions formatOption) + { + _formatOption = formatOption; + } + + /// + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem + { + // ensure all path segments of the virtual type are + // named according to the rules of this schema + if (schemaItem is IDirective directive) + { + var formattedName = this.FormatText(directive.Name, _formatOption); + schemaItem = (TSchemaItemType)directive.Clone(formattedName); + } + + return schemaItem; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyEnumNameFormatRule.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyEnumNameFormatRule.cs new file mode 100644 index 000000000..9d023a064 --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyEnumNameFormatRule.cs @@ -0,0 +1,44 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + + /// + /// Applies a given name format pattern to enum values within the target schema. + /// + internal class ApplyEnumNameFormatRule : NameFormatRuleBase, ISchemaItemFormatRule + { + private readonly TextFormatOptions _formatOption; + + /// + /// Initializes a new instance of the class. + /// + /// The format option. + public ApplyEnumNameFormatRule(TextFormatOptions formatOption) + { + _formatOption = formatOption; + } + + /// + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem + { + if (schemaItem is IEnumValue enumValue) + { + var formattedName = this.FormatText(enumValue.Name, _formatOption); + schemaItem = (TSchemaItemType)enumValue.Clone(valueName: formattedName); + } + + return schemaItem; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyFieldNameFormatRule.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyFieldNameFormatRule.cs new file mode 100644 index 000000000..32df9bfe4 --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyFieldNameFormatRule.cs @@ -0,0 +1,44 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + + /// + /// Applies a name given format pattern to each field in the targetschema. + /// + internal class ApplyFieldNameFormatRule : NameFormatRuleBase, ISchemaItemFormatRule + { + private readonly TextFormatOptions _formatOption; + + /// + /// Initializes a new instance of the class. + /// + /// The format option. + public ApplyFieldNameFormatRule(TextFormatOptions formatOption) + { + _formatOption = formatOption; + } + + /// + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem + { + if (schemaItem is IGraphField field) + { + var formattedName = this.FormatText(field.Name, _formatOption); + schemaItem = (TSchemaItemType)field.Clone(fieldName: formattedName); + } + + return schemaItem; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyFieldTypeExpressionNameFormatRule.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyFieldTypeExpressionNameFormatRule.cs new file mode 100644 index 000000000..e11f80d9e --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyFieldTypeExpressionNameFormatRule.cs @@ -0,0 +1,49 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + + /// + /// Applies a name graph type name format pattern to the type expression on each field in + /// the OBJECTs and INTERFACE types in the target schema. + /// + internal class ApplyFieldTypeExpressionNameFormatRule : NameFormatRuleBase, ISchemaItemFormatRule + { + private readonly TextFormatOptions _formatOption; + + /// + /// Initializes a new instance of the class. + /// + /// The format to use for the type name + /// referneced in the fields type expression. + public ApplyFieldTypeExpressionNameFormatRule(TextFormatOptions typeExpressionGraphTypeFormat) + { + _formatOption = typeExpressionGraphTypeFormat; + } + + /// + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem + { + if (schemaItem is IGraphField field) + { + var typeExpression = field.TypeExpression; + var formattedName = this.FormatGraphTypeName(typeExpression.TypeName, _formatOption); + typeExpression = typeExpression.Clone(formattedName); + + schemaItem = (TSchemaItemType)field.Clone(typeExpression: typeExpression); + } + + return schemaItem; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyGraphTypeNameFormatRule.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyGraphTypeNameFormatRule.cs new file mode 100644 index 000000000..db3af6897 --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyGraphTypeNameFormatRule.cs @@ -0,0 +1,63 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + using GraphQL.AspNet.Schemas.TypeSystem; + + /// + /// Applies a given format pattern to formal graph type names for the target schema. + /// + internal class ApplyGraphTypeNameFormatRule : NameFormatRuleBase, ISchemaItemFormatRule + { + private readonly TextFormatOptions _formatOption; + + /// + /// Initializes a new instance of the class. + /// + /// The format option. + public ApplyGraphTypeNameFormatRule(TextFormatOptions formatOption) + { + _formatOption = formatOption; + } + + /// + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem + { + // ensure all path segments of the virtual type are + // named according to the rules of this schema + switch (schemaItem) + { + case VirtualObjectGraphType virtualType: + var newName = VirtualObjectGraphType.MakeSafeTypeNameFromItemPath( + virtualType.ItemPathTemplate, + (segment) => this.FormatText(segment, _formatOption)); + + schemaItem = (TSchemaItemType)virtualType.Clone(newName); + break; + + // do nothing with directives + // when naming graph types + // they name under the explicit directive rule + case IDirective directive: + break; + + case IGraphType graphType: + var formattedName = this.FormatGraphTypeName(graphType.Name, _formatOption); + schemaItem = (TSchemaItemType)graphType.Clone(formattedName); + break; + } + + return schemaItem; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyGraphTypeNameFormatToUnionTypeMembersRule.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyGraphTypeNameFormatToUnionTypeMembersRule.cs new file mode 100644 index 000000000..c68ee1b9f --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyGraphTypeNameFormatToUnionTypeMembersRule.cs @@ -0,0 +1,45 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + + /// + /// Applies the given format pattern to the graph type names contained in a union type. + /// + public class ApplyGraphTypeNameFormatToUnionTypeMembersRule : NameFormatRuleBase, ISchemaItemFormatRule + { + private readonly TextFormatOptions _formatOption; + + /// + /// Initializes a new instance of the class. + /// + /// The format option. + public ApplyGraphTypeNameFormatToUnionTypeMembersRule(TextFormatOptions formatOption) + { + _formatOption = formatOption; + } + + /// + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem + { + if (schemaItem is IUnionGraphType ugt) + { + schemaItem = (TSchemaItemType)ugt.Clone( + possibleGraphTypeNameFormatter: (typeName) + => this.FormatGraphTypeName(typeName, _formatOption)); + } + + return schemaItem; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyInputFieldNameFormatRule.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyInputFieldNameFormatRule.cs new file mode 100644 index 000000000..b03545cbd --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyInputFieldNameFormatRule.cs @@ -0,0 +1,44 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + + /// + /// Applies the given format option to fields on INPUT_OBJECTs for the given schema. + /// + internal class ApplyInputFieldNameFormatRule : NameFormatRuleBase, ISchemaItemFormatRule + { + private readonly TextFormatOptions _formatOption; + + /// + /// Initializes a new instance of the class. + /// + /// The format option. + public ApplyInputFieldNameFormatRule(TextFormatOptions formatOption) + { + _formatOption = formatOption; + } + + /// + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem + { + if (schemaItem is IInputGraphField field) + { + var formattedName = this.FormatText(field.Name, _formatOption); + schemaItem = (TSchemaItemType)field.Clone(fieldName: formattedName); + } + + return schemaItem; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyInputFieldTypeExpressionNameFormatRule.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyInputFieldTypeExpressionNameFormatRule.cs new file mode 100644 index 000000000..ba6297229 --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/ApplyInputFieldTypeExpressionNameFormatRule.cs @@ -0,0 +1,49 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + + /// + /// Applies a graph type name format pattern to the type expression on each field on the INPUT_OBJECTs in + /// the target schema. + /// + internal class ApplyInputFieldTypeExpressionNameFormatRule : NameFormatRuleBase, ISchemaItemFormatRule + { + private readonly TextFormatOptions _formatOption; + + /// + /// Initializes a new instance of the class. + /// + /// The format to use for the type name + /// referenced in the fields type expression. + public ApplyInputFieldTypeExpressionNameFormatRule(TextFormatOptions typeExpressionGraphTypeFormat) + { + _formatOption = typeExpressionGraphTypeFormat; + } + + /// + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem + { + if (schemaItem is IInputGraphField field) + { + var typeExpression = field.TypeExpression; + var formattedName = this.FormatGraphTypeName(typeExpression.TypeName, _formatOption); + typeExpression = typeExpression.Clone(formattedName); + + schemaItem = (TSchemaItemType)field.Clone(typeExpression: typeExpression); + } + + return schemaItem; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/NameFormatRuleBase.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/NameFormatRuleBase.cs new file mode 100644 index 000000000..eb3a45cab --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Naming/NameFormatRuleBase.cs @@ -0,0 +1,68 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using GraphQL.AspNet.Common.Extensions; + using GraphQL.AspNet.Schemas.TypeSystem; + + /// + /// A base class for all the individual name format rules. + /// + public abstract class NameFormatRuleBase + { + /// + /// Formats or reformats the name according to the rules of this formatter. + /// + /// The name value being formatted. + /// The selected strategy to format with. + /// System.String. + protected virtual string FormatText(string name, TextFormatOptions strategy) + { + if (name == null) + return null; + + switch (strategy) + { + case TextFormatOptions.ProperCase: + return name.FirstCharacterToUpperInvariant(); + + case TextFormatOptions.CamelCase: + return name.FirstCharacterToLowerInvariant(); + + case TextFormatOptions.UpperCase: + return name.ToUpperInvariant(); + + case TextFormatOptions.LowerCase: + return name.ToLowerInvariant(); + + // ReSharper disable once RedundantCaseLabel + case TextFormatOptions.NoChanges: + default: + return name; + } + } + + /// + /// Treats the text as if it were to be a graph type name and enforces global type rule + /// rename restrictions before renaming the text value. The string is returned altered + /// or unaltered depending on its original value. + /// + /// The name to format. + /// The format to apply, if allowed. + /// System.String. + protected virtual string FormatGraphTypeName(string name, TextFormatOptions formatOption) + { + if (GlobalTypes.CanBeRenamed(name)) + name = this.FormatText(name, formatOption); + + return name; + } + } +} \ No newline at end of file diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyArgumentNonNullItemTypeExpressionFormatRule.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyArgumentNonNullItemTypeExpressionFormatRule.cs new file mode 100644 index 000000000..eb92bfcfa --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyArgumentNonNullItemTypeExpressionFormatRule.cs @@ -0,0 +1,59 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using System; + using GraphQL.AspNet.Common; + using GraphQL.AspNet.Configuration.Formatting.FormatRules.Nullability; + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + using GraphQL.AspNet.Schemas.TypeSystem; + + /// + /// For a field argument that matches the provided predicate + /// this rule will ensure that the root object of the type expression cannot be null. + /// + public class ApplyArgumentNonNullItemTypeExpressionFormatRule : NullabilityFormatRuleBase, ISchemaItemFormatRule + { + private readonly Func _predicate; + + /// + /// Initializes a new instance of the class. + /// + /// The predicate function that will be used to match an argument. + public ApplyArgumentNonNullItemTypeExpressionFormatRule(Func predicate) + { + _predicate = Validation.ThrowIfNullOrReturn(predicate, nameof(predicate)); + } + + /// + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem + { + if (schemaItem is IGraphArgument argument && _predicate(argument)) + { + var newTypeExpression = this.ConvertItemReferenceToNonNull(argument.TypeExpression); + argument = argument.Clone(typeExpression: newTypeExpression); + + if (!newTypeExpression.IsNullable && argument.HasDefaultValue && argument.DefaultValue is null) + { + // when the input argument, as a whole, becomes non-nullable and has a default value of null + // the field must become "required" without a default value because of the rules + // of the schema + argument = argument.Clone(defaultValueOptions: DefaultValueCloneOptions.MakeRequired); + } + + schemaItem = (TSchemaItemType)argument; + } + + return schemaItem; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyArgumentNonNullListTypeExpressionFormatRule.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyArgumentNonNullListTypeExpressionFormatRule.cs new file mode 100644 index 000000000..7d8384dcb --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyArgumentNonNullListTypeExpressionFormatRule.cs @@ -0,0 +1,59 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using System; + using GraphQL.AspNet.Common; + using GraphQL.AspNet.Configuration.Formatting.FormatRules.Nullability; + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + using GraphQL.AspNet.Schemas.TypeSystem; + + /// + /// For a given field argument, this rule will force all encountered lists + /// and nested lists on the type expression to be non-null. + /// + internal class ApplyArgumentNonNullListTypeExpressionFormatRule : NullabilityFormatRuleBase, ISchemaItemFormatRule + { + private readonly Func _predicate; + + /// + /// Initializes a new instance of the class. + /// + /// The predicate function that will be used to match an argument. + public ApplyArgumentNonNullListTypeExpressionFormatRule(Func predicate) + { + _predicate = Validation.ThrowIfNullOrReturn(predicate, nameof(predicate)); + } + + /// + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem + { + if (schemaItem is IGraphArgument argument && _predicate(argument)) + { + var newTypeExpression = this.ConvertAllListsToNonNull(argument.TypeExpression); + argument = argument.Clone(typeExpression: newTypeExpression); + + if (!newTypeExpression.IsNullable && argument.HasDefaultValue && argument.DefaultValue is null) + { + // when the input argument, as a whole, becomes non-nullable and has a default value of null + // the field must become "required" without a default value because of the rules + // of the schema + argument = argument.Clone(defaultValueOptions: DefaultValueCloneOptions.MakeRequired); + } + + schemaItem = (TSchemaItemType)argument; + } + + return schemaItem; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyFieldNonNullItemTypeExpressionFormatRule.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyFieldNonNullItemTypeExpressionFormatRule.cs new file mode 100644 index 000000000..38aa98c78 --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyFieldNonNullItemTypeExpressionFormatRule.cs @@ -0,0 +1,49 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using System; + using GraphQL.AspNet.Common; + using GraphQL.AspNet.Configuration.Formatting.FormatRules.Nullability; + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + + /// + /// For a given graph field (OBJECT or INTERFACE) that matches the provided predicate + /// this rule will ensure that the root object of the type expression + /// cannot be null. + /// + public class ApplyFieldNonNullItemTypeExpressionFormatRule : NullabilityFormatRuleBase, ISchemaItemFormatRule + { + private readonly Func _predicate; + + /// + /// Initializes a new instance of the class. + /// + /// The predicate function that will be used to match a graph field. + public ApplyFieldNonNullItemTypeExpressionFormatRule(Func predicate) + { + _predicate = Validation.ThrowIfNullOrReturn(predicate, nameof(predicate)); + } + + /// + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem + { + if (schemaItem is IGraphField field && _predicate(field)) + { + var newTypeExpression = this.ConvertItemReferenceToNonNull(field.TypeExpression); + schemaItem = (TSchemaItemType)field.Clone(typeExpression: newTypeExpression); + } + + return schemaItem; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyFieldNonNullListTypeExpressionFormatRule.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyFieldNonNullListTypeExpressionFormatRule.cs new file mode 100644 index 000000000..e6332a2b5 --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyFieldNonNullListTypeExpressionFormatRule.cs @@ -0,0 +1,48 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using System; + using GraphQL.AspNet.Common; + using GraphQL.AspNet.Configuration.Formatting.FormatRules.Nullability; + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + + /// + /// For a given input field (on an INPUT_OBJECT) will force all encountered lists + /// and nested lists on the type expression to be non-null. + /// + public class ApplyFieldNonNullListTypeExpressionFormatRule : NullabilityFormatRuleBase, ISchemaItemFormatRule + { + private readonly Func _predicate; + + /// + /// Initializes a new instance of the class. + /// + /// The predicate function that will be used to match a graph field. + public ApplyFieldNonNullListTypeExpressionFormatRule(Func predicate) + { + _predicate = Validation.ThrowIfNullOrReturn(predicate, nameof(predicate)); + } + + /// + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem + { + if (schemaItem is IGraphField field && _predicate(field)) + { + var newTypeExpression = this.ConvertAllListsToNonNull(field.TypeExpression); + schemaItem = (TSchemaItemType)field.Clone(typeExpression: newTypeExpression); + } + + return schemaItem; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyInputFieldNonNullItemTypeExpressionFormatRule.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyInputFieldNonNullItemTypeExpressionFormatRule.cs new file mode 100644 index 000000000..54e6dba28 --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyInputFieldNonNullItemTypeExpressionFormatRule.cs @@ -0,0 +1,60 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using System; + using GraphQL.AspNet.Common; + using GraphQL.AspNet.Configuration.Formatting.FormatRules.Nullability; + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + using GraphQL.AspNet.Schemas.TypeSystem; + + /// + /// For a given inputfield (on an INPUT_OBJECT) that matches the provided predicate + /// this rule will ensure that the root object of the type expression + /// cannot be null. + /// + public class ApplyInputFieldNonNullItemTypeExpressionFormatRule : NullabilityFormatRuleBase, ISchemaItemFormatRule + { + private readonly Func _predicate; + + /// + /// Initializes a new instance of the class. + /// + /// The predicate function that will be used to match a graph field. + public ApplyInputFieldNonNullItemTypeExpressionFormatRule(Func predicate) + { + _predicate = Validation.ThrowIfNullOrReturn(predicate, nameof(predicate)); + } + + /// + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem + { + if (schemaItem is IInputGraphField field && _predicate(field)) + { + var newTypeExpression = this.ConvertItemReferenceToNonNull(field.TypeExpression); + field = field.Clone(typeExpression: newTypeExpression); + + if (!newTypeExpression.IsNullable && field.HasDefaultValue && field.DefaultValue is null) + { + // when the field, as a whole, becomes non-nullable and has a default value of null + // the field must become "required" without a default value because of the rules + // of the schema + field = field.Clone(defaultValueOptions: DefaultValueCloneOptions.MakeRequired); + } + + schemaItem = (TSchemaItemType)field; + } + + return schemaItem; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyInputFieldNonNullListTypeExpressionFormatRule.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyInputFieldNonNullListTypeExpressionFormatRule.cs new file mode 100644 index 000000000..9b852425b --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/ApplyInputFieldNonNullListTypeExpressionFormatRule.cs @@ -0,0 +1,59 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules +{ + using System; + using GraphQL.AspNet.Common; + using GraphQL.AspNet.Configuration.Formatting.FormatRules.Nullability; + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + using GraphQL.AspNet.Schemas.TypeSystem; + + /// + /// For a given input field (on an INPUT_OBJECT) will force all encountered lists + /// and nested lists on the type expression to be non-null. + /// + internal class ApplyInputFieldNonNullListTypeExpressionFormatRule : NullabilityFormatRuleBase, ISchemaItemFormatRule + { + private readonly Func _predicate; + + /// + /// Initializes a new instance of the class. + /// + /// The predicate function that will be used to match a graph field. + public ApplyInputFieldNonNullListTypeExpressionFormatRule(Func predicate) + { + _predicate = Validation.ThrowIfNullOrReturn(predicate, nameof(predicate)); + } + + /// + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem + { + if (schemaItem is IInputGraphField field && _predicate(field)) + { + var newTypeExpression = this.ConvertAllListsToNonNull(field.TypeExpression); + field = field.Clone(typeExpression: newTypeExpression); + + if (!newTypeExpression.IsNullable && field.HasDefaultValue && field.DefaultValue is null) + { + // when the field, as a whole, becomes non-nullable and has a default value of null + // the field must become "required" without a default value because of the rules + // of the schema + field = field.Clone(defaultValueOptions: DefaultValueCloneOptions.MakeRequired); + } + + schemaItem = (TSchemaItemType)field; + } + + return schemaItem; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/NullabilityFormatRuleBase.cs b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/NullabilityFormatRuleBase.cs new file mode 100644 index 000000000..82e24dbec --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/FormatRules/Nullability/NullabilityFormatRuleBase.cs @@ -0,0 +1,47 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting.FormatRules.Nullability +{ + using GraphQL.AspNet.Schemas; + + /// + /// A common class for nullability format rules to centralize common logic + /// + public abstract class NullabilityFormatRuleBase + { + /// + /// For the given type expression converts all lists (nested or otherwise) to be + /// non-nullabe. + /// + /// The type expression. + /// GraphTypeExpression. + protected virtual GraphTypeExpression ConvertAllListsToNonNull(GraphTypeExpression typeExpression) + { + if (typeExpression.IsFixed) + return typeExpression; + + typeExpression = typeExpression.Clone(GraphTypeExpressionNullabilityStrategies.NonNullLists); + return typeExpression; + } + + /// + /// For the given type expression converts the core item reference to be non-nullable. + /// + /// The type expression. + /// GraphTypeExpression. + protected virtual GraphTypeExpression ConvertItemReferenceToNonNull(GraphTypeExpression typeExpression) + { + if (!typeExpression.IsFixed) + typeExpression = typeExpression.Clone(GraphTypeExpressionNullabilityStrategies.NonNullType); + + return typeExpression; + } + } +} diff --git a/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs index 78e53af32..be36de663 100644 --- a/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs +++ b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs @@ -9,11 +9,10 @@ namespace GraphQL.AspNet.Configuration.Formatting { - using GraphQL.AspNet.Common.Extensions; + using System.Collections.Generic; + using System.Linq; using GraphQL.AspNet.Interfaces.Configuration; using GraphQL.AspNet.Interfaces.Schema; - using GraphQL.AspNet.Schemas; - using GraphQL.AspNet.Schemas.TypeSystem; /// /// A strategy, employed by a schema, to make programatic alterations @@ -22,417 +21,70 @@ namespace GraphQL.AspNet.Configuration.Formatting public class SchemaFormatStrategy : ISchemaFormatStrategy { /// - /// Initializes a new instance of the class. + /// Creates a format strategy with all internal default rules. All name format rules + /// will be overriden with the given format option. /// - /// A single format strategy to use for all naming formats. - public SchemaFormatStrategy(SchemaItemNameFormatOptions nameFormatStrategy) - : this( - TypeExpressionNullabilityFormatRules.Default, - nameFormatStrategy, - nameFormatStrategy, - nameFormatStrategy) + /// The single format option to use for all generated names. + /// SchemaFormatStrategy. + public static ISchemaFormatStrategy CreateEmpty(TextFormatOptions formatOption) { + return CreateEmpty(formatOption, formatOption, formatOption, formatOption, formatOption); } /// - /// Initializes a new instance of the class. - /// - /// The strategy used to augment nullability - /// checks on type expressions for fields and arguments. - /// The format strategy to use for graph type names. - /// The format strategy to use for field names. - /// The format strategy to use for enum values. - public SchemaFormatStrategy( - TypeExpressionNullabilityFormatRules nullabilityStrategy = TypeExpressionNullabilityFormatRules.Default, - SchemaItemNameFormatOptions typeNameStrategy = SchemaItemNameFormatOptions.ProperCase, - SchemaItemNameFormatOptions fieldNameStrategy = SchemaItemNameFormatOptions.CamelCase, - SchemaItemNameFormatOptions enumValueStrategy = SchemaItemNameFormatOptions.UpperCase) - { - this.NullabilityStrategy = nullabilityStrategy; - this.GraphTypeNameStrategy = typeNameStrategy; - this.FieldNameStrategy = fieldNameStrategy; - this.EnumValueStrategy = enumValueStrategy; - } - - /// - public T ApplySchemaItemRules(ISchemaConfiguration configuration, T schemaItem) - where T : ISchemaItem - { - switch (schemaItem) - { - case IDirective directive: - return (T)this.FormatDirective(configuration, directive); - - case IUnionGraphType unionGraphType: - return (T)this.FormatUnionType(configuration, unionGraphType); - - case IGraphType graphType: - return (T)this.FormatGraphType(configuration, graphType); - - case IGraphField graphField: - return (T)this.FormatGraphField(configuration, graphField); - - case IEnumValue enumValue: - return (T)this.FormatEnumValue(configuration, enumValue); - - case IInputGraphField inputGraphField: - return (T)this.FormatInputGraphField(configuration, inputGraphField); - - case IGraphArgument argument: - return (T)this.FormatArgument(configuration, argument); - } - - return schemaItem; - } - - /// - /// Formats the directive name to be how it will appear in a query, excluding the '@' - /// symbol. - /// - /// The directive name to format. - /// System.String. - protected virtual string FormatDirectiveName(string name) - { - return this.FormatName(name, this.FieldNameStrategy); - } - - /// - /// Formats a field or argument name according to the rules declared on this strategy. - /// - /// The field or argument name to format. - /// System.String. - protected virtual string FormatFieldName(string name) - { - return this.FormatName(name, this.FieldNameStrategy); - } - - /// - /// Formats the enum value name according to the rules declared on this strategy. - /// - /// The enum value name to format. - /// System.String. - protected virtual string FormatEnumValueName(string name) - { - return this.FormatName(name, this.EnumValueStrategy); - } - - /// - /// Formats the graph type name according to the rules declared on this strategy. - /// - /// The type name to format. - /// System.String. - protected virtual string FormatGraphTypeName(string name) - { - if (!GlobalTypes.CanBeRenamed(name)) - return name; - - return this.FormatName(name, this.GraphTypeNameStrategy); - } - - /// - /// Applies the schema specific formats identified by - /// to the target . - /// - /// The configuration to read formatting settings - /// from. - /// The union graph type to update. - /// IDirective. - protected virtual IUnionGraphType FormatUnionType(ISchemaConfiguration configuration, IUnionGraphType unionGraphType) - { - unionGraphType.Clone(possibleGraphTypeNameFormatter: this.FormatGraphTypeName); - return (IUnionGraphType)this.FormatGraphType(configuration, unionGraphType); - } - - /// - /// Applies the schema specific formats identified by - /// to the target . - /// - /// The configuration to read formatting settings - /// from. - /// The enum value to update. - /// IDirective. - protected virtual IEnumValue FormatEnumValue(ISchemaConfiguration configuration, IEnumValue enumValue) - { - var formattedName = this.FormatEnumValueName(enumValue.Name); - return enumValue.Clone(valueName: formattedName); - } - - /// - /// Applies the schema specific formats identified by - /// to the target . - /// - /// The configuration to read formatting settings - /// from. - /// The directive to update. - /// IDirective. - protected virtual IDirective FormatDirective(ISchemaConfiguration configuration, IDirective directive) - { - // directives are referenced as fields - var formattedName = this.FormatFieldName(directive.Name); - return (IDirective)directive.Clone(formattedName); - } - - /// - /// Applies the schema specific formats identified by - /// to the target . - /// - /// The configuration to read formatting settings - /// from. - /// The graph type to update. - /// IGraphType. - protected virtual IGraphType FormatGraphType(ISchemaConfiguration configuration, IGraphType graphType) - { - // ensure all path segments of the virtual type are - // named according to the rules of this schema - if (graphType is VirtualObjectGraphType virtualType) - { - var newName = VirtualObjectGraphType.MakeSafeTypeNameFromItemPath( - virtualType.ItemPathTemplate, - this.FormatGraphTypeName); - - return virtualType.Clone(newName); - } - - var formattedName = this.FormatGraphTypeName(graphType.Name); - return graphType.Clone(formattedName); - } - - /// - /// Applies the schema specific formats identified by - /// to the target . - /// - /// The configuration to read formatting settings - /// from. - /// The field to update. - /// IInputGraphField. - protected virtual IInputGraphField FormatInputGraphField(ISchemaConfiguration configuration, IInputGraphField inputGraphField) - { - if (!inputGraphField.TypeExpression.IsFixed) - inputGraphField = this.ApplyTypeExpressionNullabilityRules(inputGraphField); - - var formattedName = this.FormatFieldName(inputGraphField.Name); - var typeExpression = inputGraphField.TypeExpression; - typeExpression = typeExpression.Clone(this.FormatGraphTypeName(typeExpression.TypeName)); - - return inputGraphField.Clone(fieldName: formattedName, typeExpression: typeExpression); - } - - /// - /// Applies the schema specific formats identified by - /// to the target . - /// - /// The configuration to read formatting settings - /// from. - /// The field to update. - /// IGraphField. - protected virtual IGraphField FormatGraphField(ISchemaConfiguration configuration, IGraphField graphField) - { - if (!graphField.TypeExpression.IsFixed) - graphField = this.ApplyTypeExpressionNullabilityRules(graphField); - - var formattedName = this.FormatFieldName(graphField.Name); - var typeExpression = graphField.TypeExpression; - typeExpression = typeExpression.Clone(this.FormatGraphTypeName(typeExpression.TypeName)); - - return graphField.Clone(fieldName: formattedName, typeExpression: typeExpression); - } - - /// - /// Applies the schema specific formats identified by - /// to the target . + /// Creates a format strategy with all internal default rules. Name format overrides + /// can be applied if needed. /// - /// The configuration to read formatting settings - /// from. - /// The argument to update. - /// IGraphField. - protected virtual IGraphArgument FormatArgument(ISchemaConfiguration configuration, IGraphArgument argument) + /// The name format to use for graph type names. + /// The name format to use for fields. + /// The name format to use for enum values. + /// The name format to use for arguments on fields and directives. + /// The name format to use for directive type names. + /// SchemaFormatStrategy. + public static ISchemaFormatStrategy CreateEmpty( + TextFormatOptions? typeNameFormat = TextFormatOptions.ProperCase, + TextFormatOptions? fieldNameFormat = TextFormatOptions.CamelCase, + TextFormatOptions? enumValueNameFormat = TextFormatOptions.UpperCase, + TextFormatOptions? argumentNameFormat = TextFormatOptions.CamelCase, + TextFormatOptions? directiveNameFormat = TextFormatOptions.CamelCase) { - if (!argument.TypeExpression.IsFixed) - argument = this.ApplyTypeExpressionNullabilityRules(argument); + var builder = new SchemaFormatStrategyBuilder() + .Clear(); - var formattedName = this.FormatFieldName(argument.Name); - var typeExpression = argument.TypeExpression; - typeExpression = typeExpression.Clone(this.FormatGraphTypeName(typeExpression.TypeName)); + if (typeNameFormat.HasValue) + builder = builder.WithGraphTypeNameFormat(typeNameFormat.Value); + if (fieldNameFormat.HasValue) + builder = builder.WithFieldNameFormat(fieldNameFormat.Value); + if (enumValueNameFormat.HasValue) + builder = builder.WithEnumValueFormat(enumValueNameFormat.Value); + if (directiveNameFormat.HasValue) + builder = builder.WithDirectiveNameFormat(directiveNameFormat.Value); + if (argumentNameFormat.HasValue) + builder = builder.WithFieldArgumentNameFormat(argumentNameFormat.Value); - return argument.Clone(argumentName: formattedName, typeExpression: typeExpression); + return builder.Build(); } - /// - /// For the given field applies an appropriate nullability strategy - /// according to the rules of this instance and returns a new instance - /// of the field. - /// - /// The graph field to update. - /// IGraphField. - protected virtual IGraphField ApplyTypeExpressionNullabilityRules(IGraphField graphField) - { - GraphTypeExpressionNullabilityStrategies strat = GraphTypeExpressionNullabilityStrategies.None; - var shouldBeNonNullType = this.NullabilityStrategy - .HasFlag(TypeExpressionNullabilityFormatRules.NonNullIntermediateTypes) - && graphField.IsVirtual; - - shouldBeNonNullType = shouldBeNonNullType || - (this.NullabilityStrategy - .HasFlag(TypeExpressionNullabilityFormatRules.NonNullOutputStrings) - && graphField.ObjectType == typeof(string)); - - shouldBeNonNullType = shouldBeNonNullType || - (this.NullabilityStrategy - .HasFlag(TypeExpressionNullabilityFormatRules.NonNullReferenceTypes) - && !graphField.IsVirtual - && graphField.ObjectType != typeof(string) - && !graphField.ObjectType.IsValueType); - - if (shouldBeNonNullType) - strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullType; - - if (this.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullLists)) - strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullLists; - - var newTypeExpression = graphField.TypeExpression.Clone(strat); - return graphField.Clone(typeExpression: newTypeExpression); - } + private List _formatRules; /// - /// For the given field, applies an appropriate nullability strategy - /// according to the rules of this instance and returns a new instance - /// of the field. - /// - /// The graph field to update. - /// IGraphField. - protected virtual IInputGraphField ApplyTypeExpressionNullabilityRules(IInputGraphField inputGraphField) - { - GraphTypeExpressionNullabilityStrategies strat = GraphTypeExpressionNullabilityStrategies.None; - - var shouldTypeBeNonNull = this.NullabilityStrategy - .HasFlag(TypeExpressionNullabilityFormatRules.NonNullInputStrings) - && inputGraphField.ObjectType == typeof(string); - - shouldTypeBeNonNull = shouldTypeBeNonNull || - (this.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullReferenceTypes) - && inputGraphField.ObjectType != typeof(string) - && !inputGraphField.ObjectType.IsValueType); - - if (shouldTypeBeNonNull) - strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullType; - - if (this.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullLists)) - strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullLists; - - var newTypeExpression = inputGraphField.TypeExpression.Clone(strat); - inputGraphField = inputGraphField.Clone(typeExpression: newTypeExpression); - - if (!newTypeExpression.IsNullable && inputGraphField.HasDefaultValue && inputGraphField.DefaultValue is null) - { - // when the field, as a whole, becomes non-nullable and has a default value of null - // the field must become "required" without a default value because of the rules - // of the schema - inputGraphField = inputGraphField.Clone(defaultValueOptions: DefaultValueCloneOptions.MakeRequired); - } - - return inputGraphField; - } - - /// - /// For the given argument applies an appropriate nullability strategy - /// according to the rules of this instance and returns a new instance - /// of the argument. + /// Initializes a new instance of the class. /// - /// The argument to update. - /// IGraphField. - protected virtual IGraphArgument ApplyTypeExpressionNullabilityRules(IGraphArgument argument) + /// The format rules to apply to schema items passing through + /// this instance. + public SchemaFormatStrategy(params ISchemaItemFormatRule[] formatRules) { - GraphTypeExpressionNullabilityStrategies strat = GraphTypeExpressionNullabilityStrategies.None; - - var shouldBeNonNullType = this.NullabilityStrategy - .HasFlag(TypeExpressionNullabilityFormatRules.NonNullInputStrings) - && argument.ObjectType == typeof(string); - - shouldBeNonNullType = shouldBeNonNullType || - (this.NullabilityStrategy - .HasFlag(TypeExpressionNullabilityFormatRules.NonNullReferenceTypes) - && argument.ObjectType != typeof(string) - && !argument.ObjectType.IsValueType); - - if (shouldBeNonNullType) - strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullType; - - if (this.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullLists)) - strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullLists; - - var newTypeExpression = argument.TypeExpression.Clone(strat); - argument = argument.Clone(typeExpression: newTypeExpression); - - if (!newTypeExpression.IsNullable && argument.HasDefaultValue && argument.DefaultValue is null) - { - // when the input argument, as a whole, becomes non-nullable and has a default value of null - // the field must become "required" without a default value because of the rules - // of the schema - argument = argument.Clone(defaultValueOptions: DefaultValueCloneOptions.MakeRequired); - } - - return argument; + _formatRules = formatRules.ToList() ?? []; } - /// - /// Formats or reformats the name according to the rules of this formatter. - /// - /// The name value being formatted. - /// The selected strategy to format with. - /// System.String. - protected virtual string FormatName(string name, SchemaItemNameFormatOptions strategy) + /// + public virtual T ApplySchemaItemRules(ISchemaConfiguration configuration, T schemaItem) + where T : ISchemaItem { - if (name == null) - return null; - - switch (strategy) - { - case SchemaItemNameFormatOptions.ProperCase: - return name.FirstCharacterToUpperInvariant(); - - case SchemaItemNameFormatOptions.CamelCase: - return name.FirstCharacterToLowerInvariant(); + foreach (var rule in _formatRules) + schemaItem = rule.Execute(schemaItem); - case SchemaItemNameFormatOptions.UpperCase: - return name.ToUpperInvariant(); - - case SchemaItemNameFormatOptions.LowerCase: - return name.ToLowerInvariant(); - - // ReSharper disable once RedundantCaseLabel - case SchemaItemNameFormatOptions.NoChanges: - default: - return name; - } + return schemaItem; } - - /// - /// Gets or sets the bitwise flags that make up the nullability strategy - /// to apply to field and argument type expressions. - /// - /// The nullability strategy. - public TypeExpressionNullabilityFormatRules NullabilityStrategy { get; set; } - - /// - /// Gets or sets the name format strategy to use for graph type names. - /// - /// The type name strategy. - public SchemaItemNameFormatOptions GraphTypeNameStrategy { get; set; } - - /// - /// Gets or sets the name format strategy to use for field names. - /// - /// The field strategy. - public SchemaItemNameFormatOptions FieldNameStrategy { get; set; } - - /// - /// Gets or sets the name format strategy to use for enum values. - /// - /// - /// The parent enum graph type, which owns an enum value, is named via - /// the . - /// - /// The enum value name strategy. - public SchemaItemNameFormatOptions EnumValueStrategy { get; set; } } } \ No newline at end of file diff --git a/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategyBuilder.cs b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategyBuilder.cs index 3d3f4073d..244371596 100644 --- a/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategyBuilder.cs +++ b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategyBuilder.cs @@ -9,11 +9,17 @@ namespace GraphQL.AspNet.Configuration.Formatting { + using System; + using System.Collections.Generic; + using GraphQL.AspNet.Attributes; + using GraphQL.AspNet.Common; + using GraphQL.AspNet.Configuration.Formatting.FormatRules; using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; /// /// A builder class to formulate a - /// with various options. + /// by applying a varied set of rules according to the devleoper's configuration requirements. /// public class SchemaFormatStrategyBuilder { @@ -26,121 +32,196 @@ public static SchemaFormatStrategyBuilder Create() return new SchemaFormatStrategyBuilder(); } - private readonly SchemaFormatStrategy _format; + private TextFormatOptions? _typeNameFormat; + private TextFormatOptions? _fieldNameFormat; + private TextFormatOptions? _enumValueFormat; + private TextFormatOptions? _argumentNameFormat; + private TextFormatOptions? _directiveNameFormat; + private List _customRules; /// /// Initializes a new instance of the class. /// public SchemaFormatStrategyBuilder() { - _format = new SchemaFormatStrategy(); + _customRules = new List(); + + _typeNameFormat = TextFormatOptions.ProperCase; + _enumValueFormat = TextFormatOptions.UpperCase; + + _fieldNameFormat = TextFormatOptions.CamelCase; + _argumentNameFormat = TextFormatOptions.CamelCase; + _directiveNameFormat = TextFormatOptions.CamelCase; + + // set all aspects of virtual fields as "non null" by default + _customRules.Add(new ApplyFieldNonNullItemTypeExpressionFormatRule(x => x.IsVirtual)); + _customRules.Add(new ApplyFieldNonNullListTypeExpressionFormatRule(x => x.IsVirtual)); } /// - /// Sets a rule such that all string scalars are marked as "not null" by default - /// when accepted as an argument to a field or returned by a field. This can be overriden on a per field or - /// per argument basis. + /// Creates a set of rules to apply the name formats requested on this builder. /// - /// SchemaFormatStrategyBuilder. - public SchemaFormatStrategyBuilder WithRequiredStrings() + /// IEnumerable<ISchemaItemFormatRule>. + protected virtual IEnumerable CreateNameFormatRules() { - return this.WithRequiredInputStrings() - .WithRequiredOutputStrings(); + // formal name formatting + if (_typeNameFormat.HasValue) + { + yield return new ApplyGraphTypeNameFormatRule(_typeNameFormat.Value); + yield return new ApplyGraphTypeNameFormatToUnionTypeMembersRule(_typeNameFormat.Value); + yield return new ApplyFieldTypeExpressionNameFormatRule(_typeNameFormat.Value); + yield return new ApplyArgumentTypeExpressionNameFormatRule(_typeNameFormat.Value); + yield return new ApplyInputFieldTypeExpressionNameFormatRule(_typeNameFormat.Value); + } + + if (_fieldNameFormat.HasValue) + { + yield return new ApplyFieldNameFormatRule(_fieldNameFormat.Value); + yield return new ApplyInputFieldNameFormatRule(_fieldNameFormat.Value); + } + + if (_directiveNameFormat.HasValue) + { + yield return new ApplyDirectiveNameFormatRule(_directiveNameFormat.Value); + } + + if (_argumentNameFormat.HasValue) + { + yield return new ApplyArgumentNameFormatRule(_argumentNameFormat.Value); + } + + if (_enumValueFormat.HasValue) + { + yield return new ApplyEnumNameFormatRule(_enumValueFormat.Value); + } } /// - /// Sets a rule such that all string scalar on inbound data items (e.g. field arguments and INPUT_OBJECT types) - /// will be declared as "not null" by default for all generated graph types.This can be overriden on a per field or - /// per argument basis. + /// Sets a rule such fields on INPUT_OBJECT types that match the given predicate + /// will be declared as "not null" by default for all generated graph types. + /// This can be overriden on a per field basis using . /// - /// GraphSchemaFormatStrategyBuilder. - public SchemaFormatStrategyBuilder WithRequiredInputStrings() + /// Example: [String] => [String!] + /// The predicate function that a field must match for the rule to be invoked. + /// SchemaFormatStrategyBuilder. + public virtual SchemaFormatStrategyBuilder DeclareInputFieldValuesAsNonNull(Func predicate) { - if (!_format.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullInputStrings)) - _format.NullabilityStrategy = _format.NullabilityStrategy | TypeExpressionNullabilityFormatRules.NonNullInputStrings; + Validation.ThrowIfNull(predicate, nameof(predicate)); - return this; + return this.DeclareCustomRule(new ApplyInputFieldNonNullItemTypeExpressionFormatRule(predicate)); } /// - /// Sets a rule such that all string scalar fields on outbound data items (e.g. INTERFACE and OBJECT types) - /// will be declared as "not null" by default for all generated graph types.This can be overriden on a per field or - /// per argument basis. + /// Sets a rule such fields on INPUT_OBJECT types that match the given predicate and return a list + /// will be declared as "not null" by default for all generated graph types. Nested lists will also + /// be made non-nullable. Matching fields that do not declare a list will be quietly skipped. + /// This can be overriden on a per field basis using . /// - /// GraphSchemaFormatStrategyBuilder. - public SchemaFormatStrategyBuilder WithRequiredOutputStrings() + /// Example: [String] => [String]! + /// The predicate function that a field must match for the rule to be invoked. + /// SchemaFormatStrategyBuilder. + public virtual SchemaFormatStrategyBuilder DeclareInputFieldListsAsNonNull(Func predicate) { - if (!_format.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullOutputStrings)) - _format.NullabilityStrategy = _format.NullabilityStrategy | TypeExpressionNullabilityFormatRules.NonNullOutputStrings; - - return this; + return this.DeclareCustomRule(new ApplyInputFieldNonNullListTypeExpressionFormatRule(predicate)); } /// - /// Sets a rule such that all List types (e.g. List, Array, IEnumerable etc.) are marked as "not null" by default - /// when accepted as an argument to a field or returned by a field. This can be overriden on a per field or - /// per argument basis. + /// Sets a rule such fields on OBJECT and INTERFACE types that match the given predicate + /// will be declared as "not null" by default for all generated graph types. + /// This can be overriden on a per field basis using . /// + /// Example: [String] => [String!] + /// The predicate function that a field must match for the rule to be invoked. /// SchemaFormatStrategyBuilder. - public SchemaFormatStrategyBuilder WithRequiredLists() + public virtual SchemaFormatStrategyBuilder DeclareFieldValuesAsNonNull(Func predicate) { - if (!_format.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullLists)) - _format.NullabilityStrategy = _format.NullabilityStrategy | TypeExpressionNullabilityFormatRules.NonNullLists; + Validation.ThrowIfNull(predicate, nameof(predicate)); + return this.DeclareCustomRule(new ApplyFieldNonNullItemTypeExpressionFormatRule(predicate)); + } - return this; + /// + /// Sets a rule such that fields on OBJECT and INTERFACE types which match the given predicate and + /// return a list of items will be declared as "not null" by default for all generated graph types. Nested lists will also + /// be made non-nullable. Matching fields that do not declare a list will be quietly skipped. + /// This can be overriden on a per field basis using . + /// + /// Example: [String] => [String]! + /// The predicate function that a field must match for the rule to be invoked. + /// SchemaFormatStrategyBuilder. + public virtual SchemaFormatStrategyBuilder DeclareFieldListsAsNonNull(Func predicate) + { + Validation.ThrowIfNull(predicate, nameof(predicate)); + return this.DeclareCustomRule(new ApplyFieldNonNullListTypeExpressionFormatRule(predicate)); } /// - /// Sets a rule such that all reference types (e.g. classes and interfaces) are marked as "not null" by default - /// when accepted as an argument to a field or returned by a field. This can be overriden on a per field or - /// per argument basis. + /// Sets a rule such that arguments on OBJECT and INTERFACE fields that match the given predicate + /// will have their object value be declared as "not null" by default for all generated graph types. + /// This can be overriden on a per field basis using . /// + /// Example: [String] => [String!] + /// The predicate function that a field must match for the rule to be invoked. /// SchemaFormatStrategyBuilder. - public SchemaFormatStrategyBuilder WithRequiredObjects() + public virtual SchemaFormatStrategyBuilder DeclareArgumentValuesAsNonNull(Func predicate) { - if (!_format.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullReferenceTypes)) - _format.NullabilityStrategy = _format.NullabilityStrategy | TypeExpressionNullabilityFormatRules.NonNullReferenceTypes; + Validation.ThrowIfNull(predicate, nameof(predicate)); + return this.DeclareCustomRule(new ApplyArgumentNonNullItemTypeExpressionFormatRule(predicate)); + } - return this; + /// + /// Sets a rule such that arguments on OBJECT and INTERFACE fields which match the given predicate and + /// return a list of items will be declared as "not null" by default for all generated graph types. Nested lists will also + /// be made non-nullable. Matching arguments that do not declare a list will be quietly skipped. + /// This can be overriden on a per field basis using . + /// + /// /// Example: [String] => [String]! + /// The predicate function that a field must match for the rule to be invoked. + /// SchemaFormatStrategyBuilder. + public virtual SchemaFormatStrategyBuilder DeclareArgumentListsAsNonNull(Func predicate) + { + Validation.ThrowIfNull(predicate, nameof(predicate)); + return this.DeclareCustomRule(new ApplyArgumentNonNullListTypeExpressionFormatRule(predicate)); } /// - /// Clears all nullability rules, including the default non-null template rule, such that - /// no rules are applied to any objects. All intermediate graph types, strings, lists are treated - /// as nullable. + /// Declares a custom built rule on the strategy. This rule will be executed against each + /// just before its added to the target schema. /// + /// The rule to declare. /// SchemaFormatStrategyBuilder. - public SchemaFormatStrategyBuilder ClearNullabilityRules() + public virtual SchemaFormatStrategyBuilder DeclareCustomRule(ISchemaItemFormatRule rule) { - _format.NullabilityStrategy = TypeExpressionNullabilityFormatRules.None; + Validation.ThrowIfNull(rule, nameof(rule)); + _customRules.Add(rule); return this; } /// - /// Sets the formating of graph type names (object, interfaces etc.) to the supplied built in strategy. + /// Sets the formating of graph type names (object, interface etc.) to the supplied built in strategy. /// /// /// DEFAULT: ProperCase /// - /// The strategy to employ for graph type names. - /// GraphSchemaFormatStrategyBuilder. - public SchemaFormatStrategyBuilder WithGraphTypeNameFormat(SchemaItemNameFormatOptions strategy) + /// The strategy to use for graph type names. + /// SchemaFormatStrategyBuilder. + public virtual SchemaFormatStrategyBuilder WithGraphTypeNameFormat(TextFormatOptions format) { - _format.GraphTypeNameStrategy = strategy; + _typeNameFormat = format; return this; } /// - /// Sets the formating of field names to the supplied built in strategy. + /// Sets the formating of field names on INPUT_OBJECT, OBJECT and INTERFACE types + /// to the supplied built in strategy. /// /// /// DEFAULT: camelCase /// - /// The strategy to employ for field names. - /// GraphSchemaFormatStrategyBuilder. - public SchemaFormatStrategyBuilder WithFieldNameFormat(SchemaItemNameFormatOptions strategy) + /// The format to use for field names. + /// SchemaFormatStrategyBuilder. + public virtual SchemaFormatStrategyBuilder WithFieldNameFormat(TextFormatOptions format) { - _format.FieldNameStrategy = strategy; + _fieldNameFormat = format; return this; } @@ -150,21 +231,71 @@ public SchemaFormatStrategyBuilder WithFieldNameFormat(SchemaItemNameFormatOptio /// /// Default: UPPER CASE /// - /// The strategy to employ for graph type names. - /// GraphSchemaFormatStrategyBuilder. - public SchemaFormatStrategyBuilder WithEnumValueFormat(SchemaItemNameFormatOptions strategy) + /// The strategy to use for graph type names. + /// SchemaFormatStrategyBuilder. + public virtual SchemaFormatStrategyBuilder WithEnumValueFormat(TextFormatOptions format) + { + _enumValueFormat = format; + return this; + } + + /// + /// Sets the formating of argument names defined on fields in INTERFACE and OBJECT + /// graph types as well as arguments on DIRECTIVEs. + /// + /// + /// Default: camelCase + /// + /// The format to use for all field argument names. + /// SchemaFormatStrategyBuilder. + public virtual SchemaFormatStrategyBuilder WithFieldArgumentNameFormat(TextFormatOptions format) + { + _argumentNameFormat = format; + return this; + } + + /// + /// Sets the formating of directive names for the target schema (e.g. @camelCasedDirectiveName) + /// + /// + /// Default: camelCase + /// + /// The format to use for all directive names. + /// SchemaFormatStrategyBuilder. + public virtual SchemaFormatStrategyBuilder WithDirectiveNameFormat(TextFormatOptions format) + { + _directiveNameFormat = format; + return this; + } + + /// + /// Clears all custom rules and name format options. + /// + /// SchemaFormatStrategyBuilder. + public SchemaFormatStrategyBuilder Clear() { - _format.EnumValueStrategy = strategy; + _customRules.Clear(); + + _typeNameFormat = null; + _fieldNameFormat = null; + _enumValueFormat = null; + _argumentNameFormat = null; + _directiveNameFormat = null; + return this; } /// - /// Returns the format strategy instance being built. + /// Creates the format strategy from all the rules and options set on this builder. /// - /// GraphSchemaFormatStrategy. - public ISchemaFormatStrategy Build() + /// ISchemaFormatStrategy. + public virtual ISchemaFormatStrategy Build() { - return _format; + var ruleSet = new List(); + ruleSet.AddRange(this.CreateNameFormatRules()); + ruleSet.AddRange(_customRules); + + return new SchemaFormatStrategy(ruleSet.ToArray()); } } } \ No newline at end of file diff --git a/src/graphql-aspnet/Configuration/Formatting/SchemaItemNameFormatOptions.cs b/src/graphql-aspnet/Configuration/Formatting/SchemaItemNameFormatOptions.cs index 95c8662bb..ac00b307e 100644 --- a/src/graphql-aspnet/Configuration/Formatting/SchemaItemNameFormatOptions.cs +++ b/src/graphql-aspnet/Configuration/Formatting/SchemaItemNameFormatOptions.cs @@ -13,12 +13,31 @@ namespace GraphQL.AspNet.Configuration.Formatting /// A set of internally supported name formatting options for various names and values created /// for a schema. /// - public enum SchemaItemNameFormatOptions + public enum TextFormatOptions { + /// + /// Text is rendered with a captial first letter (e.g. ItemNumber1, ItemNumber2). + /// ProperCase, + + /// + /// Text is rendered with a lower case first letter (e.g. itemNumber1, itemNumber2). + /// CamelCase, + + /// + /// Text is rendered in all upper case letters (e.g. ITEMNUMBER1, ITEMNUMBER2). + /// UpperCase, + + /// + /// Text is rendered in all lower case letters (e.g. itemnumber1, itemnumber2). + /// LowerCase, + + /// + /// Text is rendered as provided, no text changes are made. + /// NoChanges, } } \ No newline at end of file diff --git a/src/graphql-aspnet/Configuration/Formatting/TypeExpressionNullabilityFormatRules.cs b/src/graphql-aspnet/Configuration/Formatting/TypeExpressionNullabilityFormatRules.cs deleted file mode 100644 index e17554cc7..000000000 --- a/src/graphql-aspnet/Configuration/Formatting/TypeExpressionNullabilityFormatRules.cs +++ /dev/null @@ -1,72 +0,0 @@ -// ************************************************************* -// project: graphql-aspnet -// -- -// repo: https://github.com/graphql-aspnet -// docs: https://graphql-aspnet.github.io -// -- -// License: MIT -// ************************************************************* - -namespace GraphQL.AspNet.Configuration.Formatting -{ - using System; - - /// - /// A bitwise set of format strategies that can be applied out of the box. - /// - [Flags] - public enum TypeExpressionNullabilityFormatRules - { - /// - /// No changes are made to the nullability of different fields. They are used as provided in - /// the source code. All strings and reference types passed to an argument or returned - /// from a field are considered nullable unless otherwise overriden. - /// - None = 0, - - /// - /// Intermediate graph types, created when you use path templates on your - /// queries and mutations, are marked as non-nullable. This can greatly assist - /// in reducing null-handling noise in various client side code generators. Intermediate graph - /// types will never be null but may be expressed as "nullable" in the schema if this is not set. - /// - NonNullIntermediateTypes = 1, - - /// - /// All lists, in any type expression, be that as input arguments - /// or fields are treated as non-nullable by default. - /// - NonNullLists = 2, - - /// - /// String scalars on input items (i.e. field arguments and INPUT_OBJECT fields) are treated like - /// value types and considered "not nullable" by default. - /// - NonNullInputStrings = 4, - - /// - /// String scalars on output items (i.e. fields on OBJECT and INTERFACE types) are treated like - /// value types and considered "not nullable" by default. - /// - NonNullOutputStrings = 8, - - /// - /// The schema will treat all class reference types, in any type expression, as being non-nullable by - /// default. This option DOES NOT include the type. - /// - NonNullReferenceTypes = 16, - - /// - /// All string scalars, whether as a field argument, input object field, object field or interface field - /// are treated like value types and are considered "not nullable" by default in all instances. - /// - NonNullStrings = NonNullInputStrings | NonNullOutputStrings, - - /// - /// No changes are made to the nullability of different fields. They are used as provided in - /// the source code. All strings and reference types passed to an argument or returned - /// from a field are considered nullable unless otherwise overriden. - /// - Default = NonNullIntermediateTypes, - } -} \ No newline at end of file diff --git a/src/graphql-aspnet/Configuration/SchemaDeclarationConfiguration.cs b/src/graphql-aspnet/Configuration/SchemaDeclarationConfiguration.cs index 45f0c4a31..212cd9993 100644 --- a/src/graphql-aspnet/Configuration/SchemaDeclarationConfiguration.cs +++ b/src/graphql-aspnet/Configuration/SchemaDeclarationConfiguration.cs @@ -11,7 +11,6 @@ namespace GraphQL.AspNet.Configuration { using System.Collections.Generic; using System.Diagnostics; - using GraphQL.AspNet.Configuration.Formatting; using GraphQL.AspNet.Interfaces.Configuration; using GraphQL.AspNet.Interfaces.Schema; using GraphQL.AspNet.Schemas.TypeSystem; @@ -32,6 +31,7 @@ public SchemaDeclarationConfiguration() this.AllowedOperations.Add(GraphOperationType.Query); this.AllowedOperations.Add(GraphOperationType.Mutation); this.ArgumentBindingRule = SchemaArgumentBindingRules.ParametersPreferQueryResolution; + this.SchemaFormatStrategy = Formatting.SchemaFormatStrategy.CreateEmpty(); } /// @@ -65,7 +65,7 @@ public void Merge(ISchemaDeclarationConfiguration config) public TemplateDeclarationRequirements FieldDeclarationRequirements { get; set; } = TemplateDeclarationRequirements.Default; /// - public ISchemaFormatStrategy SchemaFormatStrategy { get; set; } = new SchemaFormatStrategy(); + public ISchemaFormatStrategy SchemaFormatStrategy { get; set; } /// public HashSet AllowedOperations { get; } diff --git a/src/graphql-aspnet/Directives/Global/IncludeDirective.cs b/src/graphql-aspnet/Directives/Global/IncludeDirective.cs index 55fcbf301..a81c85896 100644 --- a/src/graphql-aspnet/Directives/Global/IncludeDirective.cs +++ b/src/graphql-aspnet/Directives/Global/IncludeDirective.cs @@ -32,7 +32,7 @@ public sealed class IncludeDirective : GraphDirective /// IGraphActionResult. [DirectiveLocations(DirectiveLocation.FIELD | DirectiveLocation.FRAGMENT_SPREAD | DirectiveLocation.INLINE_FRAGMENT)] public IGraphActionResult Execute( - [FromGraphQL("if")] + [FromGraphQL("if", TypeExpression = "Type!")] [Description("When true, the field or fragment is included in the query results.")] bool ifArgument) { diff --git a/src/graphql-aspnet/Directives/Global/SkipDirective.cs b/src/graphql-aspnet/Directives/Global/SkipDirective.cs index 90e78f15e..a28489213 100644 --- a/src/graphql-aspnet/Directives/Global/SkipDirective.cs +++ b/src/graphql-aspnet/Directives/Global/SkipDirective.cs @@ -32,7 +32,7 @@ public sealed class SkipDirective : GraphDirective /// IGraphActionResult. [DirectiveLocations(DirectiveLocation.FIELD | DirectiveLocation.FRAGMENT_SPREAD | DirectiveLocation.INLINE_FRAGMENT)] public IGraphActionResult Execute( - [FromGraphQL("if")] + [FromGraphQL("if", TypeExpression = "Type!")] [Description("When true, the field or fragment is excluded from the query results.")] bool ifArgument) { diff --git a/src/graphql-aspnet/Interfaces/Configuration/ISchemaItemFormatRule.cs b/src/graphql-aspnet/Interfaces/Configuration/ISchemaItemFormatRule.cs index a9a90eeb5..e1caaba8c 100644 --- a/src/graphql-aspnet/Interfaces/Configuration/ISchemaItemFormatRule.cs +++ b/src/graphql-aspnet/Interfaces/Configuration/ISchemaItemFormatRule.cs @@ -20,10 +20,10 @@ public interface ISchemaItemFormatRule /// /// Executes this rule against the target schema item /// - /// The actual type of the schema item being processed. + /// The actual type of the schema item being processed. /// The schema item. /// The original schema item or a modified version of the schema item. - public T Execute(T schemaItem) - where T : ISchemaItem; + public TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem; } } diff --git a/src/graphql-aspnet/Schemas/Generation/TypeTemplates/GraphDirectiveMethodTemplate.cs b/src/graphql-aspnet/Schemas/Generation/TypeTemplates/GraphDirectiveMethodTemplate.cs index ff359004b..37b5eca5f 100644 --- a/src/graphql-aspnet/Schemas/Generation/TypeTemplates/GraphDirectiveMethodTemplate.cs +++ b/src/graphql-aspnet/Schemas/Generation/TypeTemplates/GraphDirectiveMethodTemplate.cs @@ -22,7 +22,6 @@ namespace GraphQL.AspNet.Schemas.Generation.TypeTemplates using GraphQL.AspNet.Common.Extensions; using GraphQL.AspNet.Execution.Exceptions; using GraphQL.AspNet.Execution.Resolvers; - using GraphQL.AspNet.Execution.RulesEngine.RuleSets.DocumentValidation.QueryOperationSteps; using GraphQL.AspNet.Interfaces.Controllers; using GraphQL.AspNet.Interfaces.Execution; using GraphQL.AspNet.Interfaces.Internal; @@ -76,7 +75,6 @@ public virtual void Parse() this.DeclaredType = this.Method.ReturnType; this.ObjectType = GraphValidation.EliminateWrappersFromCoreType(this.DeclaredType); this.TypeExpression = new GraphTypeExpression(this.ObjectType.FriendlyName()); - this.Description = this.AttributeProvider.SingleAttributeOrDefault()?.Description; this.IsAsyncField = Validation.IsCastable(this.Method.ReturnType); this.AppliedDirectives = this.ExtractAppliedDirectiveTemplates(); diff --git a/src/graphql-aspnet/Schemas/TypeSystem/Introspection/Model/IntrospectedType.cs b/src/graphql-aspnet/Schemas/TypeSystem/Introspection/Model/IntrospectedType.cs index 8ce45b326..2d94d612f 100644 --- a/src/graphql-aspnet/Schemas/TypeSystem/Introspection/Model/IntrospectedType.cs +++ b/src/graphql-aspnet/Schemas/TypeSystem/Introspection/Model/IntrospectedType.cs @@ -14,7 +14,6 @@ namespace GraphQL.AspNet.Schemas.TypeSystem.Introspection.Model using System.Diagnostics; using System.Linq; using GraphQL.AspNet.Common; - using GraphQL.AspNet.Common.Generics; using GraphQL.AspNet.Execution.Exceptions; using GraphQL.AspNet.Interfaces.Schema; using GraphQL.AspNet.Schemas.TypeSystem; diff --git a/src/graphql-aspnet/Schemas/TypeSystem/VirtualGraphField.cs b/src/graphql-aspnet/Schemas/TypeSystem/VirtualGraphField.cs index 4b80e8a7a..97b28820f 100644 --- a/src/graphql-aspnet/Schemas/TypeSystem/VirtualGraphField.cs +++ b/src/graphql-aspnet/Schemas/TypeSystem/VirtualGraphField.cs @@ -73,6 +73,8 @@ public VirtualGraphField( this.Publish = true; this.IsDeprecated = false; this.DeprecationReason = null; + + this.TypeExpression = this.TypeExpression.ToFixed(); } /// diff --git a/src/graphql-aspnet/graphql-aspnet.csproj b/src/graphql-aspnet/graphql-aspnet.csproj index 8eeb495d4..f8b4e8493 100644 --- a/src/graphql-aspnet/graphql-aspnet.csproj +++ b/src/graphql-aspnet/graphql-aspnet.csproj @@ -18,8 +18,4 @@ - - - - \ No newline at end of file diff --git a/src/unit-tests/graphql-aspnet-testframework/GraphQLSchemaHelperMethods.cs b/src/unit-tests/graphql-aspnet-testframework/GraphQLSchemaHelperMethods.cs index ed452a7ca..1129ef851 100644 --- a/src/unit-tests/graphql-aspnet-testframework/GraphQLSchemaHelperMethods.cs +++ b/src/unit-tests/graphql-aspnet-testframework/GraphQLSchemaHelperMethods.cs @@ -9,11 +9,9 @@ namespace GraphQL.AspNet.Tests.Framework { - using System; using GraphQL.AspNet.Configuration; using GraphQL.AspNet.Configuration.Formatting; using GraphQL.AspNet.Interfaces.Schema; - using GraphQL.AspNet.Schemas.TypeSystem; /// /// Methods for quickly configuring schema related things during testing. @@ -30,7 +28,7 @@ public static void SetNoAlterationConfiguration(this ISchema schema) { var declarationOptions = new SchemaDeclarationConfiguration(); declarationOptions.Merge(schema.Configuration.DeclarationOptions); - declarationOptions.SchemaFormatStrategy = new SchemaFormatStrategy(SchemaItemNameFormatOptions.NoChanges); + declarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(TextFormatOptions.NoChanges); var config = new SchemaConfiguration( declarationOptions, diff --git a/src/unit-tests/graphql-aspnet-testframework/TestServerBuilder{TSchema}.cs b/src/unit-tests/graphql-aspnet-testframework/TestServerBuilder{TSchema}.cs index 4e08584e1..ce67f1960 100644 --- a/src/unit-tests/graphql-aspnet-testframework/TestServerBuilder{TSchema}.cs +++ b/src/unit-tests/graphql-aspnet-testframework/TestServerBuilder{TSchema}.cs @@ -105,7 +105,7 @@ protected virtual void PerformInitialConfiguration(SchemaOptions options) if (_initialSetup.HasFlag(TestOptions.UseCodeDeclaredNames)) { - options.DeclarationOptions.SchemaFormatStrategy = new SchemaFormatStrategy(SchemaItemNameFormatOptions.NoChanges); + options.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(TextFormatOptions.NoChanges); } if (_initialSetup.HasFlag(TestOptions.IncludeExceptions)) diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationFieldNamingTests.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationFieldNamingTests.cs index f0f544c53..2c2673b2d 100644 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationFieldNamingTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationFieldNamingTests.cs @@ -52,13 +52,13 @@ public async Task ProductionDefaults_UpperCaseFieldNames() builder.AddGraphQL(o => { - o.DeclarationOptions.SchemaFormatStrategy = new SchemaFormatStrategy(fieldNameStrategy: SchemaItemNameFormatOptions.UpperCase); + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(fieldNameFormat: TextFormatOptions.UpperCase); }); var server = builder.Build(); var queryBuilder = server.CreateQueryContextBuilder(); - queryBuilder.AddQueryText("{ RETRIEVEFAN(NAME: \"bob\"){ ID, NAME, FANSPEED} }"); + queryBuilder.AddQueryText("{ RETRIEVEFAN(name: \"bob\"){ ID, NAME, FANSPEED} }"); var result = await server.RenderResult(queryBuilder).ConfigureAwait(false); @@ -84,7 +84,7 @@ public async Task ProductionDefaults_LowerCaseEnumValues() builder.AddGraphQL(o => { - o.DeclarationOptions.SchemaFormatStrategy = new SchemaFormatStrategy(enumValueStrategy: SchemaItemNameFormatOptions.LowerCase); + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(enumValueNameFormat: TextFormatOptions.LowerCase); }); var server = builder.Build(); @@ -116,7 +116,7 @@ public async Task ProductionDefaults_CamelCasedTypeNames() builder.AddGraphQL(o => { - o.DeclarationOptions.SchemaFormatStrategy = new SchemaFormatStrategy(typeNameStrategy: SchemaItemNameFormatOptions.CamelCase); + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(typeNameFormat: TextFormatOptions.CamelCase); }); var server = builder.Build(); diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/IWidgetInterface.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/IWidget.cs similarity index 73% rename from src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/IWidgetInterface.cs rename to src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/IWidget.cs index 0339112b8..0272f5192 100644 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/IWidgetInterface.cs +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/IWidget.cs @@ -11,8 +11,14 @@ namespace GraphQL.AspNet.Tests.Configuration.FormatStrategyTestData { using GraphQL.AspNet.Attributes; - public interface IWidgetInterface + public interface IWidget { + [GraphField] + string ArgItem(string arg1); + + [GraphField] + string FixedArgItem([FromGraphQL(TypeExpression = "Type")] string arg1); + int IntProp { get; set; } [GraphField(TypeExpression = "Type")] @@ -26,9 +32,9 @@ public interface IWidgetInterface [GraphField(TypeExpression = "Type")] string FixedStringProp { get; set; } - IWidgetInterface ReferenceProp { get; set; } + IWidget ReferenceProp { get; set; } [GraphField(TypeExpression = "Type")] - IWidgetInterface FixedReferenceProp { get; set; } + IWidget FixedReferenceProp { get; set; } } } \ No newline at end of file diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/IWidgetList.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/IWidgetList.cs new file mode 100644 index 000000000..0fcc73c9e --- /dev/null +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/IWidgetList.cs @@ -0,0 +1,28 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Tests.Configuration.FormatStrategyTestData +{ + using System.Collections.Generic; + using GraphQL.AspNet.Attributes; + + public interface IWidgetList + { + [GraphField] + string TripleListArg(List>> arg1); + + [GraphField] + string TripleListArgFixed([FromGraphQL(TypeExpression = "[[[Type]]]")] List>> arg1); + + List>> TripleListProp { get; set; } + + [GraphField(TypeExpression = "[[[Type]]]")] + List>> TripleListPropFixed { get; set; } + } +} \ No newline at end of file diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/Widget.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/Widget.cs index 1c7866b82..ad142f8ad 100644 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/Widget.cs +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/Widget.cs @@ -13,6 +13,18 @@ namespace GraphQL.AspNet.Tests.Configuration.FormatStrategyTestData public class Widget { + [GraphField] + public string ArgItem(string arg1) + { + return string.Empty; + } + + [GraphField] + public string FixedArgItem([FromGraphQL(TypeExpression = "Type")] string arg1) + { + return string.Empty; + } + public int IntProp { get; set; } [GraphField(TypeExpression = "Type")] diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetArgumentController.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetArgumentController.cs index 853641faa..010183291 100644 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetArgumentController.cs +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetArgumentController.cs @@ -15,37 +15,13 @@ namespace GraphQL.AspNet.Tests.Configuration.FormatStrategyTestData public class WidgetArgumentController : GraphController { [QueryRoot] - public Widget IntArgument(int arg1) + public Widget WithArgument(string arg1) { return new Widget(); } - [QueryRoot] - public Widget IntArgumentFixed([FromGraphQL(TypeExpression = "Type!")] int arg1) - { - return new Widget(); - } - - [QueryRoot] - public Widget StringArgument(string arg1) - { - return new Widget(); - } - - [QueryRoot] - public Widget StringArgumentFixed([FromGraphQL(TypeExpression = "Type")] string arg1) - { - return new Widget(); - } - - [QueryRoot] - public Widget InputObjectArgument(Widget arg1) - { - return new Widget(); - } - - [QueryRoot] - public Widget InputObjectArgumentFixed([FromGraphQL(TypeExpression = "Type")] Widget arg1) + [QueryRoot(TypeExpression = "Type")] + public Widget WithArgumentFixed([FromGraphQL(TypeExpression = "Type")] string arg1) { return new Widget(); } diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetDirective.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetDirective.cs new file mode 100644 index 000000000..868c54876 --- /dev/null +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetDirective.cs @@ -0,0 +1,26 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Tests.Configuration.FormatStrategyTestData +{ + using GraphQL.AspNet.Attributes; + using GraphQL.AspNet.Directives; + using GraphQL.AspNet.Interfaces.Controllers; + using GraphQL.AspNet.Schemas.TypeSystem; + + [GraphType("widgetD")] + public class WidgetDirective : GraphDirective + { + [DirectiveLocations(DirectiveLocation.FIELD | DirectiveLocation.FRAGMENT_SPREAD | DirectiveLocation.INLINE_FRAGMENT)] + public IGraphActionResult Execute() + { + return this.Ok(); + } + } +} diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetList.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetList.cs index 9ed9ebe3d..02dc15a48 100644 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetList.cs +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetList.cs @@ -14,9 +14,21 @@ namespace GraphQL.AspNet.Tests.Configuration.FormatStrategyTestData public class WidgetList { - public List>> TripleListProp { get; set; } + [GraphField] + public string TripleListArg(List>> arg1) + { + return string.Empty; + } + + [GraphField] + public string TripleListArgFixed([FromGraphQL(TypeExpression = "[[[Type]]]")] List>> arg1) + { + return string.Empty; + } [GraphField(TypeExpression = "[[[Type]]]")] public List>> TripleListPropFixed { get; set; } + + public List>> TripleListProp { get; set; } } } \ No newline at end of file diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetListController.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetListController.cs index f803431af..ae23bf535 100644 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetListController.cs +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetListController.cs @@ -22,19 +22,19 @@ public Widget IntArgument(List arg1) } [QueryRoot] - public Widget IntArgumentFixed([FromGraphQL(TypeExpression = "[Type!]")]List arg1) + public Widget IntArgumentFixed([FromGraphQL(TypeExpression = "[Type!]")] List arg1) { return new Widget(); } [QueryRoot] - public Widget StringArgument(List arg1) + public Widget StringListArgument(List arg1) { return new Widget(); } [QueryRoot] - public Widget StringArgumentFixed([FromGraphQL(TypeExpression = "[Type]")] List arg1) + public Widget StringListArgumentFixed([FromGraphQL(TypeExpression = "[Type]")] List arg1) { return new Widget(); } @@ -50,5 +50,29 @@ public Widget InputObjectArgumentFixed([FromGraphQL(TypeExpression = "[Type]")] { return new Widget(); } + + [QueryRoot] + public Widget WidgetField() + { + return null; + } + + [QueryRoot(TypeExpression = "Type")] + public Widget WidgetFieldFixed() + { + return null; + } + + [QueryRoot] + public List ReturnedStringList() + { + return null; + } + + [QueryRoot(TypeExpression = "[Type]")] + public List ReturnedStringListFixed() + { + return null; + } } } \ No newline at end of file diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetType.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetType.cs new file mode 100644 index 000000000..aca99c34d --- /dev/null +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/FormatStrategyTestData/WidgetType.cs @@ -0,0 +1,16 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Tests.Configuration.FormatStrategyTestData +{ + public enum WidgetType + { + Type1, + } +} diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFomatStrategyTests.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFomatStrategyTests.cs deleted file mode 100644 index 4a05c04a7..000000000 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFomatStrategyTests.cs +++ /dev/null @@ -1,422 +0,0 @@ -// ************************************************************* -// project: graphql-aspnet -// -- -// repo: https://github.com/graphql-aspnet -// docs: https://graphql-aspnet.github.io -// -- -// License: MIT -// ************************************************************* - -namespace GraphQL.AspNet.Tests.Configuration -{ - using System; - using GraphQL.AspNet.Configuration.Formatting; - using GraphQL.AspNet.Interfaces.Schema; - using GraphQL.AspNet.Schemas.TypeSystem; - using GraphQL.AspNet.Tests.Configuration.FormatStrategyTestData; - using GraphQL.AspNet.Tests.Framework; - using NUnit.Framework; - using NullRules = GraphQL.AspNet.Configuration.Formatting.TypeExpressionNullabilityFormatRules; - - [TestFixture] - public class GraphSchemaFomatStrategyTests - { - // value type, not fixed, object graph type - [TestCase(NullRules.None, typeof(Widget), "intProp", "Int!")] - [TestCase(NullRules.NonNullStrings, typeof(Widget), "intProp", "Int!")] - [TestCase(NullRules.NonNullLists, typeof(Widget), "intProp", "Int!")] - [TestCase(NullRules.NonNullIntermediateTypes, typeof(Widget), "intProp", "Int!")] - [TestCase(NullRules.NonNullReferenceTypes, typeof(Widget), "intProp", "Int!")] - - // value type, fixed as nullable, object graph type - [TestCase(NullRules.None, typeof(Widget), "fixedIntPropAsNullable", "Int")] - [TestCase(NullRules.NonNullStrings, typeof(Widget), "fixedIntPropAsNullable", "Int")] - [TestCase(NullRules.NonNullLists, typeof(Widget), "fixedIntPropAsNullable", "Int")] - [TestCase(NullRules.NonNullIntermediateTypes, typeof(Widget), "fixedIntPropAsNullable", "Int")] - [TestCase(NullRules.NonNullReferenceTypes, typeof(Widget), "fixedIntPropAsNullable", "Int")] - - // value type, fixed as not nullable, object graph type - [TestCase(NullRules.None, typeof(Widget), "fixedIntPropAsNotNullable", "Int!")] - [TestCase(NullRules.NonNullStrings, typeof(Widget), "fixedIntPropAsNotNullable", "Int!")] - [TestCase(NullRules.NonNullLists, typeof(Widget), "fixedIntPropAsNotNullable", "Int!")] - [TestCase(NullRules.NonNullIntermediateTypes, typeof(Widget), "fixedIntPropAsNotNullable", "Int!")] - [TestCase(NullRules.NonNullReferenceTypes, typeof(Widget), "fixedIntPropAsNotNullable", "Int!")] - - // string type, not fixed, object graph type - [TestCase(NullRules.None, typeof(Widget), "stringProp", "String")] - [TestCase(NullRules.NonNullStrings, typeof(Widget), "stringProp", "String!")] - [TestCase(NullRules.NonNullInputStrings, typeof(Widget), "stringProp", "String")] - [TestCase(NullRules.NonNullOutputStrings, typeof(Widget), "stringProp", "String!")] - [TestCase(NullRules.NonNullLists, typeof(Widget), "stringProp", "String")] - [TestCase(NullRules.NonNullIntermediateTypes, typeof(Widget), "stringProp", "String")] - [TestCase(NullRules.NonNullReferenceTypes, typeof(Widget), "stringProp", "String")] - - // string, fixed, object graph type - [TestCase(NullRules.None, typeof(Widget), "fixedStringProp", "String")] - [TestCase(NullRules.NonNullStrings, typeof(Widget), "fixedStringProp", "String")] - [TestCase(NullRules.NonNullInputStrings, typeof(Widget), "fixedStringProp", "String")] - [TestCase(NullRules.NonNullOutputStrings, typeof(Widget), "fixedStringProp", "String")] - [TestCase(NullRules.NonNullLists, typeof(Widget), "fixedStringProp", "String")] - [TestCase(NullRules.NonNullIntermediateTypes, typeof(Widget), "fixedStringProp", "String")] - [TestCase(NullRules.NonNullReferenceTypes, typeof(Widget), "fixedStringProp", "String")] - - // object reference, not fixed, object graph type - [TestCase(NullRules.None, typeof(Widget), "referenceProp", "Widget")] - [TestCase(NullRules.NonNullStrings, typeof(Widget), "referenceProp", "Widget")] - [TestCase(NullRules.NonNullLists, typeof(Widget), "referenceProp", "Widget")] - [TestCase(NullRules.NonNullIntermediateTypes, typeof(Widget), "referenceProp", "Widget")] - [TestCase(NullRules.NonNullReferenceTypes, typeof(Widget), "referenceProp", "Widget!")] - - // object reference, fixed, object graph type - [TestCase(NullRules.None, typeof(Widget), "fixedReferenceProp", "Widget")] - [TestCase(NullRules.NonNullStrings, typeof(Widget), "fixedReferenceProp", "Widget")] - [TestCase(NullRules.NonNullLists, typeof(Widget), "fixedReferenceProp", "Widget")] - [TestCase(NullRules.NonNullIntermediateTypes, typeof(Widget), "fixedReferenceProp", "Widget")] - [TestCase(NullRules.NonNullReferenceTypes, typeof(Widget), "fixedReferenceProp", "Widget")] - - // value type, not fixed, interface graph type - [TestCase(NullRules.None, typeof(IWidgetInterface), "intProp", "Int!")] - [TestCase(NullRules.NonNullStrings, typeof(IWidgetInterface), "intProp", "Int!")] - [TestCase(NullRules.NonNullLists, typeof(IWidgetInterface), "intProp", "Int!")] - [TestCase(NullRules.NonNullIntermediateTypes, typeof(IWidgetInterface), "intProp", "Int!")] - [TestCase(NullRules.NonNullReferenceTypes, typeof(IWidgetInterface), "intProp", "Int!")] - - // value type, fixed as nullable, interface graph type - [TestCase(NullRules.None, typeof(IWidgetInterface), "fixedIntPropAsNullable", "Int")] - [TestCase(NullRules.NonNullStrings, typeof(IWidgetInterface), "fixedIntPropAsNullable", "Int")] - [TestCase(NullRules.NonNullLists, typeof(IWidgetInterface), "fixedIntPropAsNullable", "Int")] - [TestCase(NullRules.NonNullIntermediateTypes, typeof(IWidgetInterface), "fixedIntPropAsNullable", "Int")] - [TestCase(NullRules.NonNullReferenceTypes, typeof(IWidgetInterface), "fixedIntPropAsNullable", "Int")] - - // value type, fixed as not nullable, interface graph type - [TestCase(NullRules.None, typeof(IWidgetInterface), "fixedIntPropAsNotNullable", "Int!")] - [TestCase(NullRules.NonNullStrings, typeof(IWidgetInterface), "fixedIntPropAsNotNullable", "Int!")] - [TestCase(NullRules.NonNullLists, typeof(IWidgetInterface), "fixedIntPropAsNotNullable", "Int!")] - [TestCase(NullRules.NonNullIntermediateTypes, typeof(IWidgetInterface), "fixedIntPropAsNotNullable", "Int!")] - [TestCase(NullRules.NonNullReferenceTypes, typeof(IWidgetInterface), "fixedIntPropAsNotNullable", "Int!")] - - // string type, not fixed, interface graph type - [TestCase(NullRules.None, typeof(IWidgetInterface), "stringProp", "String")] - [TestCase(NullRules.NonNullStrings, typeof(IWidgetInterface), "stringProp", "String!")] - [TestCase(NullRules.NonNullInputStrings, typeof(IWidgetInterface), "stringProp", "String")] - [TestCase(NullRules.NonNullOutputStrings, typeof(IWidgetInterface), "stringProp", "String!")] - [TestCase(NullRules.NonNullLists, typeof(IWidgetInterface), "stringProp", "String")] - [TestCase(NullRules.NonNullIntermediateTypes, typeof(IWidgetInterface), "stringProp", "String")] - [TestCase(NullRules.NonNullReferenceTypes, typeof(IWidgetInterface), "stringProp", "String")] - - // string, fixed, interface graph type - [TestCase(NullRules.None, typeof(IWidgetInterface), "fixedStringProp", "String")] - [TestCase(NullRules.NonNullStrings, typeof(IWidgetInterface), "fixedStringProp", "String")] - [TestCase(NullRules.NonNullInputStrings, typeof(IWidgetInterface), "fixedStringProp", "String")] - [TestCase(NullRules.NonNullOutputStrings, typeof(IWidgetInterface), "fixedStringProp", "String")] - [TestCase(NullRules.NonNullLists, typeof(IWidgetInterface), "fixedStringProp", "String")] - [TestCase(NullRules.NonNullIntermediateTypes, typeof(IWidgetInterface), "fixedStringProp", "String")] - [TestCase(NullRules.NonNullReferenceTypes, typeof(IWidgetInterface), "fixedStringProp", "String")] - - // object reference, not fixed, interface graph type - [TestCase(NullRules.None, typeof(IWidgetInterface), "referenceProp", "IWidgetInterface")] - [TestCase(NullRules.NonNullStrings, typeof(IWidgetInterface), "referenceProp", "IWidgetInterface")] - [TestCase(NullRules.NonNullLists, typeof(IWidgetInterface), "referenceProp", "IWidgetInterface")] - [TestCase(NullRules.NonNullIntermediateTypes, typeof(IWidgetInterface), "referenceProp", "IWidgetInterface")] - [TestCase(NullRules.NonNullReferenceTypes, typeof(IWidgetInterface), "referenceProp", "IWidgetInterface!")] - - // object reference, fixed, interface graph type - [TestCase(NullRules.None, typeof(IWidgetInterface), "fixedReferenceProp", "IWidgetInterface")] - [TestCase(NullRules.NonNullStrings, typeof(IWidgetInterface), "fixedReferenceProp", "IWidgetInterface")] - [TestCase(NullRules.NonNullLists, typeof(IWidgetInterface), "fixedReferenceProp", "IWidgetInterface")] - [TestCase(NullRules.NonNullIntermediateTypes, typeof(IWidgetInterface), "fixedReferenceProp", "IWidgetInterface")] - [TestCase(NullRules.NonNullReferenceTypes, typeof(IWidgetInterface), "fixedReferenceProp", "IWidgetInterface")] - public void Integration_ConcreteObjectTypes_FieldTypeExpressionTests( - NullRules strategy, - Type concreteType, - string fieldName, - string expectedTypeExpression) - { - var formatter = new SchemaFormatStrategy(); - formatter.NullabilityStrategy = strategy; - - var server = new TestServerBuilder() - .AddGraphQL(o => - { - o.AddType(concreteType); - o.DeclarationOptions.SchemaFormatStrategy = formatter; - }) - .Build(); - - var graphType = server.Schema.KnownTypes.FindGraphType(concreteType) as IGraphFieldContainer; - var field = graphType.Fields[fieldName]; - - Assert.AreEqual(expectedTypeExpression, field.TypeExpression.ToString()); - } - - // top level templated item returning a virtual type - [TestCase(NullRules.None, "Query_Widgets", "path1", "Query_Widgets_Path1")] - [TestCase(NullRules.NonNullIntermediateTypes, "Query_Widgets", "path1", "Query_Widgets_Path1!")] - [TestCase(NullRules.NonNullStrings, "Query_Widgets", "path1", "Query_Widgets_Path1")] - [TestCase(NullRules.NonNullInputStrings, "Query_Widgets", "path1", "Query_Widgets_Path1")] - [TestCase(NullRules.NonNullOutputStrings, "Query_Widgets", "path1", "Query_Widgets_Path1")] - [TestCase(NullRules.NonNullLists, "Query_Widgets", "path1", "Query_Widgets_Path1")] - [TestCase(NullRules.NonNullReferenceTypes, "Query_Widgets", "path1", "Query_Widgets_Path1")] - - // nested templated item returning a virtual type - [TestCase(NullRules.None, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] - [TestCase(NullRules.NonNullIntermediateTypes, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2!")] - [TestCase(NullRules.NonNullStrings, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] - [TestCase(NullRules.NonNullInputStrings, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] - [TestCase(NullRules.NonNullLists, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] - [TestCase(NullRules.NonNullReferenceTypes, "Query_Widgets_Path1", "path2", "Query_Widgets_Path1_Path2")] - public void VirtualFields_FieldTypeExpressionNullabilityTests( - NullRules strategy, - string graphTypeName, - string fieldName, - string expectedTypeExpression) - { - var formatter = new SchemaFormatStrategy(); - formatter.NullabilityStrategy = strategy; - - var server = new TestServerBuilder() - .AddGraphQL(o => - { - o.AddController(); - o.DeclarationOptions.SchemaFormatStrategy = formatter; - }) - .Build(); - - var graphType = server.Schema.KnownTypes.FindGraphType(graphTypeName) as IGraphFieldContainer; - var field = graphType.Fields[fieldName]; - - Assert.AreEqual(expectedTypeExpression, field.TypeExpression.ToString()); - } - - [TestCase(NullRules.None, "intArgument", "Int!")] - [TestCase(NullRules.NonNullIntermediateTypes, "intArgument", "Int!")] - [TestCase(NullRules.NonNullStrings, "intArgument", "Int!")] - [TestCase(NullRules.NonNullInputStrings, "intArgument", "Int!")] - [TestCase(NullRules.NonNullOutputStrings, "intArgument", "Int!")] - [TestCase(NullRules.NonNullReferenceTypes, "intArgument", "Int!")] - - [TestCase(NullRules.None, "intArgumentFixed", "Int!")] - [TestCase(NullRules.NonNullIntermediateTypes, "intArgumentFixed", "Int!")] - [TestCase(NullRules.NonNullStrings, "intArgumentFixed", "Int!")] - [TestCase(NullRules.NonNullInputStrings, "intArgumentFixed", "Int!")] - [TestCase(NullRules.NonNullOutputStrings, "intArgumentFixed", "Int!")] - [TestCase(NullRules.NonNullReferenceTypes, "intArgumentFixed", "Int!")] - - [TestCase(NullRules.None, "stringArgument", "String")] - [TestCase(NullRules.NonNullIntermediateTypes, "stringArgument", "String")] - [TestCase(NullRules.NonNullStrings, "stringArgument", "String!")] - [TestCase(NullRules.NonNullInputStrings, "stringArgument", "String!")] - [TestCase(NullRules.NonNullOutputStrings, "stringArgument", "String")] - [TestCase(NullRules.NonNullReferenceTypes, "stringArgument", "String")] - - [TestCase(NullRules.None, "stringArgumentFixed", "String")] - [TestCase(NullRules.NonNullIntermediateTypes, "stringArgumentFixed", "String")] - [TestCase(NullRules.NonNullStrings, "stringArgumentFixed", "String")] - [TestCase(NullRules.NonNullInputStrings, "stringArgumentFixed", "String")] - [TestCase(NullRules.NonNullOutputStrings, "stringArgumentFixed", "String")] - [TestCase(NullRules.NonNullReferenceTypes, "stringArgumentFixed", "String")] - - [TestCase(NullRules.None, "inputObjectArgument", "Input_Widget")] - [TestCase(NullRules.NonNullIntermediateTypes, "inputObjectArgument", "Input_Widget")] - [TestCase(NullRules.NonNullStrings, "inputObjectArgument", "Input_Widget")] - [TestCase(NullRules.NonNullInputStrings, "inputObjectArgument", "Input_Widget")] - [TestCase(NullRules.NonNullOutputStrings, "inputObjectArgument", "Input_Widget")] - [TestCase(NullRules.NonNullReferenceTypes, "inputObjectArgument", "Input_Widget!")] - - [TestCase(NullRules.None, "inputObjectArgumentFixed", "Input_Widget")] - [TestCase(NullRules.NonNullIntermediateTypes, "inputObjectArgumentFixed", "Input_Widget")] - [TestCase(NullRules.NonNullStrings, "inputObjectArgumentFixed", "Input_Widget")] - [TestCase(NullRules.NonNullInputStrings, "inputObjectArgumentFixed", "Input_Widget")] - [TestCase(NullRules.NonNullOutputStrings, "inputObjectArgumentFixed", "Input_Widget")] - [TestCase(NullRules.NonNullReferenceTypes, "inputObjectArgumentFixed", "Input_Widget")] - public void Integration_FieldArgumentTypeExpressionNullabilityTests( - NullRules strategy, - string fieldName, - string expectedTypeExpression) - { - var formatter = new SchemaFormatStrategy(); - formatter.NullabilityStrategy = strategy; - - var server = new TestServerBuilder() - .AddGraphQL(o => - { - o.AddController(); - o.DeclarationOptions.SchemaFormatStrategy = formatter; - }) - .Build(); - - var graphType = server.Schema.KnownTypes.FindGraphType("Query") as IGraphFieldContainer; - var field = graphType.Fields[fieldName]; - var arg1 = field.Arguments["arg1"]; - - Assert.AreEqual(expectedTypeExpression, arg1.TypeExpression.ToString()); - } - - [TestCase(NullRules.None, TypeKind.OBJECT, "tripleListProp", "[[[String]]]")] - [TestCase(NullRules.NonNullStrings, TypeKind.OBJECT, "tripleListProp", "[[[String!]]]")] - [TestCase(NullRules.NonNullInputStrings, TypeKind.OBJECT, "tripleListProp", "[[[String]]]")] - [TestCase(NullRules.NonNullOutputStrings, TypeKind.OBJECT, "tripleListProp", "[[[String!]]]")] - [TestCase(NullRules.NonNullReferenceTypes, TypeKind.OBJECT, "tripleListProp", "[[[String]]]")] - [TestCase(NullRules.NonNullLists, TypeKind.OBJECT, "tripleListProp", "[[[String]!]!]!")] - [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, TypeKind.OBJECT, "tripleListProp", "[[[String!]!]!]!")] - - [TestCase(NullRules.None, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NullRules.NonNullStrings, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NullRules.NonNullInputStrings, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NullRules.NonNullOutputStrings, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NullRules.NonNullLists, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NullRules.NonNullReferenceTypes, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] - - [TestCase(NullRules.None, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]]]")] - [TestCase(NullRules.NonNullStrings, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String!]]]")] - [TestCase(NullRules.NonNullInputStrings, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String!]]]")] - [TestCase(NullRules.NonNullOutputStrings, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]]]")] - [TestCase(NullRules.NonNullReferenceTypes, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]]]")] - [TestCase(NullRules.NonNullLists, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]!]!]!")] - [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String!]!]!]!")] - - [TestCase(NullRules.None, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NullRules.NonNullStrings, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NullRules.NonNullInputStrings, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NullRules.NonNullOutputStrings, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NullRules.NonNullLists, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NullRules.NonNullReferenceTypes, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] - [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] - public void Integration_Objects_FieldTypeExpressionListNullabilityTests( - NullRules strategy, - TypeKind typeKind, - string fieldName, - string expectedTypeExpression) - { - var formatter = new SchemaFormatStrategy(); - formatter.NullabilityStrategy = strategy; - - var server = new TestServerBuilder() - .AddGraphQL(o => - { - o.AddType(typeKind); - o.DeclarationOptions.SchemaFormatStrategy = formatter; - }) - .Build(); - - if (typeKind == TypeKind.OBJECT) - { - var graphType = server.Schema.KnownTypes.FindGraphType(typeof(WidgetList)) as IGraphFieldContainer; - var field = graphType.Fields[fieldName]; - Assert.AreEqual(expectedTypeExpression, field.TypeExpression.ToString()); - } - else if (typeKind == TypeKind.INPUT_OBJECT) - { - var graphType = server.Schema.KnownTypes.FindGraphType(typeof(WidgetList)) as IInputObjectGraphType; - var field = graphType.Fields[fieldName]; - Assert.AreEqual(expectedTypeExpression, field.TypeExpression.ToString()); - } - } - - [TestCase(NullRules.None, "intArgument", "[Int!]")] - [TestCase(NullRules.NonNullStrings, "intArgument", "[Int!]")] - [TestCase(NullRules.NonNullLists, "intArgument", "[Int!]!")] - [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, "intArgument", "[Int!]!")] - - [TestCase(NullRules.None, "intArgumentFixed", "[Int!]")] - [TestCase(NullRules.NonNullStrings, "intArgumentFixed", "[Int!]")] - [TestCase(NullRules.NonNullLists, "intArgumentFixed", "[Int!]")] - [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, "intArgumentFixed", "[Int!]")] - - [TestCase(NullRules.None, "stringArgument", "[String]")] - [TestCase(NullRules.NonNullStrings, "stringArgument", "[String!]")] - [TestCase(NullRules.NonNullInputStrings, "stringArgument", "[String!]")] - [TestCase(NullRules.NonNullOutputStrings, "stringArgument", "[String]")] - [TestCase(NullRules.NonNullLists, "stringArgument", "[String]!")] - [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, "stringArgument", "[String!]!")] - - [TestCase(NullRules.None, "stringArgumentFixed", "[String]")] - [TestCase(NullRules.NonNullStrings, "stringArgumentFixed", "[String]")] - [TestCase(NullRules.NonNullInputStrings, "stringArgumentFixed", "[String]")] - [TestCase(NullRules.NonNullOutputStrings, "stringArgumentFixed", "[String]")] - [TestCase(NullRules.NonNullLists, "stringArgumentFixed", "[String]")] - [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, "stringArgumentFixed", "[String]")] - - [TestCase(NullRules.None, "inputObjectArgument", "[Input_Widget]")] - [TestCase(NullRules.NonNullStrings, "inputObjectArgument", "[Input_Widget]")] - [TestCase(NullRules.NonNullReferenceTypes, "inputObjectArgument", "[Input_Widget!]")] - [TestCase(NullRules.NonNullLists, "inputObjectArgument", "[Input_Widget]!")] - [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, "inputObjectArgument", "[Input_Widget]!")] - [TestCase(NullRules.NonNullLists | NullRules.NonNullReferenceTypes, "inputObjectArgument", "[Input_Widget!]!")] - - [TestCase(NullRules.None, "inputObjectArgumentFixed", "[Input_Widget]")] - [TestCase(NullRules.NonNullStrings, "inputObjectArgumentFixed", "[Input_Widget]")] - [TestCase(NullRules.NonNullReferenceTypes, "inputObjectArgumentFixed", "[Input_Widget]")] - [TestCase(NullRules.NonNullLists, "inputObjectArgumentFixed", "[Input_Widget]")] - [TestCase(NullRules.NonNullLists | NullRules.NonNullStrings, "inputObjectArgumentFixed", "[Input_Widget]")] - [TestCase(NullRules.NonNullLists | NullRules.NonNullReferenceTypes, "inputObjectArgumentFixed", "[Input_Widget]")] - public void Integration_Controllers_FieldTypeExpressionListNullabilityTests( - NullRules strategy, - string fieldName, - string expectedTypeExpression) - { - var formatter = new SchemaFormatStrategy(); - formatter.NullabilityStrategy = strategy; - - var server = new TestServerBuilder() - .AddGraphQL(o => - { - o.AddController(); - o.DeclarationOptions.SchemaFormatStrategy = formatter; - }) - .Build(); - - var graphType = server.Schema.KnownTypes.FindGraphType("Query") as IGraphFieldContainer; - var field = graphType.Fields[fieldName]; - var arg1 = field.Arguments["arg1"]; - - Assert.AreEqual(expectedTypeExpression, arg1.TypeExpression.ToString()); - } - - [Test] - public void InputObject_NullableField_WithNonNullDefault_DefaultIsRetained_WhenFieldMadeNonNull() - { - var formatter = new SchemaFormatStrategy(); - formatter.NullabilityStrategy = NullRules.NonNullInputStrings; - - var server = new TestServerBuilder() - .AddGraphQL(o => - { - o.AddType(TypeKind.INPUT_OBJECT); - o.DeclarationOptions.SchemaFormatStrategy = formatter; - }) - .Build(); - - var graphType = server.Schema.KnownTypes.FindGraphType($"Input_{nameof(WidgetWithDefaultValue)}") as IInputObjectGraphType; - var field = graphType.Fields["stringProp"]; - - Assert.AreEqual("String!", field.TypeExpression.ToString()); - Assert.IsTrue(field.HasDefaultValue); - Assert.AreEqual("default 1", field.DefaultValue.ToString()); - } - - [Test] - public void FieldArgument_WithNonNullDefault_DefaultIsRetained_WhenFieldMadeNonNull() - { - var formatter = new SchemaFormatStrategy(); - formatter.NullabilityStrategy = NullRules.NonNullInputStrings; - - var server = new TestServerBuilder() - .AddGraphQL(o => - { - o.AddController(); - o.DeclarationOptions.SchemaFormatStrategy = formatter; - }) - .Build(); - - var graphType = server.Schema.KnownTypes.FindGraphType("Query") as IGraphFieldContainer; - var field = graphType.Fields["retrieveRootWidget"]; - var arg1 = field.Arguments["arg1"]; - - Assert.AreEqual("String!", arg1.TypeExpression.ToString()); - Assert.IsTrue(arg1.HasDefaultValue); - Assert.AreEqual("default 1", arg1.DefaultValue.ToString()); - } - } -} \ No newline at end of file diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFormatStrategyBuilderTests.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFormatStrategyBuilderTests.cs deleted file mode 100644 index 848260965..000000000 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFormatStrategyBuilderTests.cs +++ /dev/null @@ -1,120 +0,0 @@ -// ************************************************************* -// project: graphql-aspnet -// -- -// repo: https://github.com/graphql-aspnet -// docs: https://graphql-aspnet.github.io -// -- -// License: MIT -// ************************************************************* - -namespace GraphQL.AspNet.Tests.Configuration -{ - using GraphQL.AspNet.Configuration.Formatting; - using NUnit.Framework; - - [TestFixture] - public class GraphSchemaFormatStrategyBuilderTests - { - [Test] - public void EnsureDefaults() - { - var strat = (SchemaFormatStrategy)SchemaFormatStrategyBuilder - .Create() - .Build(); - - Assert.AreEqual(SchemaItemNameFormatOptions.ProperCase, strat.GraphTypeNameStrategy); - Assert.AreEqual(SchemaItemNameFormatOptions.CamelCase, strat.FieldNameStrategy); - Assert.AreEqual(SchemaItemNameFormatOptions.UpperCase, strat.EnumValueStrategy); - Assert.AreEqual(TypeExpressionNullabilityFormatRules.Default, strat.NullabilityStrategy); - } - - [Test] - public void WithGraphTypeNameFormat_UpdatesOnlyTypeNameStrategy() - { - var strat = (SchemaFormatStrategy)SchemaFormatStrategyBuilder - .Create() - .WithGraphTypeNameFormat(SchemaItemNameFormatOptions.NoChanges) - .Build(); - - Assert.AreEqual(SchemaItemNameFormatOptions.NoChanges, strat.GraphTypeNameStrategy); - Assert.AreEqual(SchemaItemNameFormatOptions.CamelCase, strat.FieldNameStrategy); - Assert.AreEqual(SchemaItemNameFormatOptions.UpperCase, strat.EnumValueStrategy); - } - - [Test] - public void WithFieldNameFormat_UpdatesOnlyFieldNameStrategy() - { - var strat = (SchemaFormatStrategy)SchemaFormatStrategyBuilder - .Create() - .WithFieldNameFormat(SchemaItemNameFormatOptions.NoChanges) - .Build(); - - Assert.AreEqual(SchemaItemNameFormatOptions.ProperCase, strat.GraphTypeNameStrategy); - Assert.AreEqual(SchemaItemNameFormatOptions.NoChanges, strat.FieldNameStrategy); - Assert.AreEqual(SchemaItemNameFormatOptions.UpperCase, strat.EnumValueStrategy); - } - - [Test] - public void WithEnumValueFormat_UpdatesOnlyEnumValueStrategy() - { - var strat = (SchemaFormatStrategy)SchemaFormatStrategyBuilder - .Create() - .WithEnumValueFormat(SchemaItemNameFormatOptions.NoChanges) - .Build(); - - Assert.AreEqual(SchemaItemNameFormatOptions.ProperCase, strat.GraphTypeNameStrategy); - Assert.AreEqual(SchemaItemNameFormatOptions.CamelCase, strat.FieldNameStrategy); - Assert.AreEqual(SchemaItemNameFormatOptions.NoChanges, strat.EnumValueStrategy); - } - - [Test] - public void ClearNullabilityRules_UpdatesNullabilityStrategy() - { - var strat = (SchemaFormatStrategy)SchemaFormatStrategyBuilder - .Create() - .ClearNullabilityRules() - .Build(); - - Assert.AreEqual(TypeExpressionNullabilityFormatRules.None, strat.NullabilityStrategy); - } - - [Test] - public void WithRequiredObjects_UpdatesNullabilityStrategy() - { - var strat = (SchemaFormatStrategy)SchemaFormatStrategyBuilder - .Create() - .WithRequiredObjects() - .Build(); - - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullIntermediateTypes)); - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullReferenceTypes)); - } - - [Test] - public void WithRequiredStrings_UpdatesNullabilityStrategy() - { - var strat = (SchemaFormatStrategy)SchemaFormatStrategyBuilder - .Create() - .WithRequiredStrings() - .Build(); - - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullIntermediateTypes)); - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullStrings)); - } - - [Test] - public void WithRequiredLists_UpdatesNullabilityStrategy() - { - var strat = (SchemaFormatStrategy)SchemaFormatStrategyBuilder - .Create() - .WithRequiredLists() - .Build(); - - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullIntermediateTypes)); - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(TypeExpressionNullabilityFormatRules.NonNullLists)); - } - } -} - -// Change everything on the formatters over to rules -// figure out how to get rid of the name formatter (probably not possible) \ No newline at end of file diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/SchemaFormatStrategy_NameFormatTests.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/SchemaFormatStrategy_NameFormatTests.cs new file mode 100644 index 000000000..5b7587874 --- /dev/null +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/SchemaFormatStrategy_NameFormatTests.cs @@ -0,0 +1,223 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Tests.Configuration +{ + using System; + using GraphQL.AspNet.Configuration.Formatting; + using GraphQL.AspNet.Interfaces.Schema; + using GraphQL.AspNet.Schemas.TypeSystem; + using GraphQL.AspNet.Tests.Configuration.FormatStrategyTestData; + using GraphQL.AspNet.Tests.Framework; + using NUnit.Framework; + + [TestFixture] + public class SchemaFormatStrategy_NameFormatTests + { + [TestCase("TYPE1", TextFormatOptions.UpperCase)] + [TestCase("type1", TextFormatOptions.LowerCase)] + [TestCase("Type1", TextFormatOptions.ProperCase)] + public void EnumValues_FormatTests( + string expectedValueName, + TextFormatOptions enumValueFormat) + { + var strategy = SchemaFormatStrategyBuilder.Create() + .WithEnumValueFormat(enumValueFormat) + .Build(); + + var server = new TestServerBuilder() + .AddGraphQL(o => + { + o.AddType(); + o.DeclarationOptions.SchemaFormatStrategy = strategy; + }) + .Build(); + + var enumType = server.Schema.KnownTypes.FindGraphType(typeof(WidgetType)) as IEnumGraphType; + var firstValue = enumType.Values.FindByEnumValue(WidgetType.Type1); + + Assert.AreEqual(expectedValueName, firstValue.Name); + } + + [TestCase(typeof(Widget), TypeKind.OBJECT, TextFormatOptions.ProperCase, "Widget")] + [TestCase(typeof(Widget), TypeKind.OBJECT, TextFormatOptions.UpperCase, "WIDGET")] + [TestCase(typeof(Widget), TypeKind.OBJECT, TextFormatOptions.CamelCase, "widget")] + [TestCase(typeof(Widget), TypeKind.OBJECT, TextFormatOptions.LowerCase, "widget")] + [TestCase(typeof(Widget), TypeKind.INPUT_OBJECT, TextFormatOptions.ProperCase, "Input_Widget")] + [TestCase(typeof(Widget), TypeKind.INPUT_OBJECT, TextFormatOptions.CamelCase, "input_Widget")] + [TestCase(typeof(Widget), TypeKind.INPUT_OBJECT, TextFormatOptions.UpperCase, "INPUT_WIDGET")] + [TestCase(typeof(Widget), TypeKind.INPUT_OBJECT, TextFormatOptions.LowerCase, "input_widget")] + [TestCase(typeof(IWidget), TypeKind.OBJECT, TextFormatOptions.ProperCase, "IWidget")] + [TestCase(typeof(IWidget), TypeKind.OBJECT, TextFormatOptions.CamelCase, "iWidget")] + [TestCase(typeof(IWidget), TypeKind.INTERFACE, TextFormatOptions.UpperCase, "IWIDGET")] + [TestCase(typeof(IWidget), TypeKind.INTERFACE, TextFormatOptions.LowerCase, "iwidget")] + [TestCase(typeof(WidgetType), TypeKind.ENUM, TextFormatOptions.ProperCase, "WidgetType")] + [TestCase(typeof(WidgetType), TypeKind.ENUM, TextFormatOptions.CamelCase, "widgetType")] + [TestCase(typeof(WidgetType), TypeKind.ENUM, TextFormatOptions.UpperCase, "WIDGETTYPE")] + [TestCase(typeof(WidgetType), TypeKind.ENUM, TextFormatOptions.LowerCase, "widgettype")] + + // directive name is uneffected by graph type name formats + [TestCase(typeof(WidgetDirective), TypeKind.DIRECTIVE, TextFormatOptions.ProperCase, "widgetD")] + [TestCase(typeof(WidgetDirective), TypeKind.DIRECTIVE, TextFormatOptions.UpperCase, "widgetD")] + [TestCase(typeof(WidgetDirective), TypeKind.DIRECTIVE, TextFormatOptions.LowerCase, "widgetD")] + public void GraphTypeNames_FormatTests( + Type targetType, + TypeKind typeKind, + TextFormatOptions typeNameFormat, + string expectedName) + { + var strategy = SchemaFormatStrategyBuilder.Create() + .WithGraphTypeNameFormat(typeNameFormat) + .Build(); + + var server = new TestServerBuilder() + .AddGraphQL(o => + { + o.AddType(targetType, typeKind); + o.DeclarationOptions.SchemaFormatStrategy = strategy; + }) + .Build(); + + var graphType = server.Schema.KnownTypes.FindGraphType(targetType); + + Assert.AreEqual(expectedName, graphType.Name); + } + + [TestCase(typeof(Widget), TypeKind.OBJECT, TextFormatOptions.ProperCase, "IntProp")] + [TestCase(typeof(Widget), TypeKind.OBJECT, TextFormatOptions.CamelCase, "intProp")] + [TestCase(typeof(Widget), TypeKind.OBJECT, TextFormatOptions.UpperCase, "INTPROP")] + [TestCase(typeof(Widget), TypeKind.OBJECT, TextFormatOptions.LowerCase, "intprop")] + [TestCase(typeof(Widget), TypeKind.INPUT_OBJECT, TextFormatOptions.ProperCase, "IntProp")] + [TestCase(typeof(Widget), TypeKind.INPUT_OBJECT, TextFormatOptions.CamelCase, "intProp")] + [TestCase(typeof(Widget), TypeKind.INPUT_OBJECT, TextFormatOptions.UpperCase, "INTPROP")] + [TestCase(typeof(Widget), TypeKind.INPUT_OBJECT, TextFormatOptions.LowerCase, "intprop")] + [TestCase(typeof(IWidget), TypeKind.INTERFACE, TextFormatOptions.ProperCase, "IntProp")] + [TestCase(typeof(IWidget), TypeKind.INTERFACE, TextFormatOptions.CamelCase, "intProp")] + [TestCase(typeof(IWidget), TypeKind.INTERFACE, TextFormatOptions.UpperCase, "INTPROP")] + [TestCase(typeof(IWidget), TypeKind.INTERFACE, TextFormatOptions.LowerCase, "intprop")] + public void FieldNames_FormatTests( + Type targetType, + TypeKind typeKind, + TextFormatOptions typeNameFormat, + string expectedName) + { + var strategy = SchemaFormatStrategyBuilder.Create() + .WithFieldNameFormat(typeNameFormat) + .Build(); + + var server = new TestServerBuilder() + .AddGraphQL(o => + { + o.AddType(targetType, typeKind); + o.DeclarationOptions.SchemaFormatStrategy = strategy; + }) + .Build(); + + var graphType = server.Schema.KnownTypes.FindGraphType(targetType); + + switch (typeKind) + { + case TypeKind.INPUT_OBJECT: + Assert.IsNotNull(((IInputObjectGraphType)graphType).Fields.FindField(expectedName)); + return; + + case TypeKind.OBJECT: + case TypeKind.INTERFACE: + Assert.IsNotNull(((IGraphFieldContainer)graphType).Fields.FindField(expectedName)); + return; + } + } + + // directive name is uneffected by field name formats + [TestCase(typeof(WidgetDirective), TypeKind.DIRECTIVE, TextFormatOptions.ProperCase, "WidgetD")] + [TestCase(typeof(WidgetDirective), TypeKind.DIRECTIVE, TextFormatOptions.CamelCase, "widgetD")] + [TestCase(typeof(WidgetDirective), TypeKind.DIRECTIVE, TextFormatOptions.UpperCase, "WIDGETD")] + [TestCase(typeof(WidgetDirective), TypeKind.DIRECTIVE, TextFormatOptions.LowerCase, "widgetd")] + public void DirectiveNames_FormatTests( + Type targetType, + TypeKind typeKind, + TextFormatOptions typeNameFormat, + string expectedName) + { + var strategy = SchemaFormatStrategyBuilder.Create() + .WithDirectiveNameFormat(typeNameFormat) + .Build(); + + var server = new TestServerBuilder() + .AddGraphQL(o => + { + o.AddType(targetType, typeKind); + o.DeclarationOptions.SchemaFormatStrategy = strategy; + }) + .Build(); + + var directive = server.Schema.KnownTypes.FindGraphType(targetType) as IDirective; + + Assert.AreEqual(expectedName, directive.Name); + } + + [TestCase(typeof(Widget), TypeKind.OBJECT, TextFormatOptions.ProperCase, "argItem", "Arg1")] + [TestCase(typeof(Widget), TypeKind.OBJECT, TextFormatOptions.CamelCase, "argItem", "arg1")] + [TestCase(typeof(Widget), TypeKind.OBJECT, TextFormatOptions.UpperCase, "argItem", "ARG1")] + [TestCase(typeof(Widget), TypeKind.OBJECT, TextFormatOptions.LowerCase, "argItem", "arg1")] + [TestCase(typeof(IWidget), TypeKind.INTERFACE, TextFormatOptions.ProperCase, "argItem", "Arg1")] + [TestCase(typeof(IWidget), TypeKind.INTERFACE, TextFormatOptions.CamelCase, "argItem", "arg1")] + [TestCase(typeof(IWidget), TypeKind.INTERFACE, TextFormatOptions.UpperCase, "argItem", "ARG1")] + [TestCase(typeof(IWidget), TypeKind.INTERFACE, TextFormatOptions.LowerCase, "argItem", "arg1")] + public void ArgumentNames_FormatTests( + Type targetType, + TypeKind typeKind, + TextFormatOptions typeNameFormat, + string fieldName, + string expectedArgName) + { + var strategy = SchemaFormatStrategyBuilder.Create() + .WithFieldArgumentNameFormat(typeNameFormat) + .Build(); + + var server = new TestServerBuilder() + .AddGraphQL(o => + { + o.AddType(targetType, typeKind); + o.DeclarationOptions.SchemaFormatStrategy = strategy; + }) + .Build(); + + var graphType = server.Schema.KnownTypes.FindGraphType(targetType) as IGraphFieldContainer; + var field = graphType.Fields[fieldName]; + + Assert.IsNotNull(field.Arguments.FindArgument(expectedArgName)); + } + + [TestCase(TextFormatOptions.ProperCase, "RetrieveRootWidget")] + [TestCase(TextFormatOptions.CamelCase, "retrieveRootWidget")] + [TestCase(TextFormatOptions.UpperCase, "RETRIEVEROOTWIDGET")] + [TestCase(TextFormatOptions.LowerCase, "retrieverootwidget")] + public void Controller_FieldNames_FormatTests( + TextFormatOptions typeNameFormat, + string expectedFieldName) + { + var strategy = SchemaFormatStrategyBuilder.Create() + .WithFieldNameFormat(typeNameFormat) + .Build(); + + var server = new TestServerBuilder() + .AddGraphQL(o => + { + o.AddType(); + o.DeclarationOptions.SchemaFormatStrategy = strategy; + }) + .Build(); + + var fieldSet = server.Schema.KnownTypes.FindGraphType("Query") as IGraphFieldContainer; + + Assert.IsNotNull(fieldSet.Fields.FindField(expectedFieldName)); + } + } +} diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/SchemaFormatStrategy_NonNullListTests.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/SchemaFormatStrategy_NonNullListTests.cs new file mode 100644 index 000000000..a496c255c --- /dev/null +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/SchemaFormatStrategy_NonNullListTests.cs @@ -0,0 +1,139 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Tests.Configuration +{ + using System; + using GraphQL.AspNet.Configuration.Formatting; + using GraphQL.AspNet.Interfaces.Schema; + using GraphQL.AspNet.Schemas.TypeSystem; + using GraphQL.AspNet.Tests.Configuration.FormatStrategyTestData; + using GraphQL.AspNet.Tests.Framework; + using NUnit.Framework; + + [TestFixture] + public class SchemaFormatStrategy_NonNullListTests + { + [TestCase(typeof(WidgetList), TypeKind.INPUT_OBJECT, "tripleListProp", "[[[String]!]!]!")] + [TestCase(typeof(WidgetList), TypeKind.INPUT_OBJECT, "tripleListPropFixed", "[[[String]]]")] + public void DeclareInputFieldListsAsNonNull_TypeExpressionsAreUpdated( + Type targetType, + TypeKind typeKind, + string fieldName, + string expectedTypeExpression) + { + ExecuteStrategyTest( + targetType, + typeKind, + fieldName, + string.Empty, + expectedTypeExpression, + x => x.DeclareInputFieldListsAsNonNull(x => true)); + } + + [TestCase(typeof(WidgetList), TypeKind.OBJECT, "tripleListProp", "[[[String]!]!]!")] + [TestCase(typeof(WidgetList), TypeKind.OBJECT, "tripleListPropFixed", "[[[String]]]")] + [TestCase(typeof(IWidgetList), TypeKind.INTERFACE, "tripleListProp", "[[[String]!]!]!")] + [TestCase(typeof(IWidgetList), TypeKind.INTERFACE, "tripleListPropFixed", "[[[String]]]")] + [TestCase(typeof(WidgetListController), TypeKind.CONTROLLER, "returnedStringList", "[String]!")] + [TestCase(typeof(WidgetListController), TypeKind.CONTROLLER, "returnedStringListFixed", "[String]")] + public void DeclareFieldListsAsNonNull_TypeExpressionsAreUpdated( + Type targetType, + TypeKind typeKind, + string fieldName, + string expectedTypeExpression) + { + ExecuteStrategyTest( + targetType, + typeKind, + fieldName, + string.Empty, + expectedTypeExpression, + x => x.DeclareFieldListsAsNonNull(x => true)); + } + + [TestCase(typeof(WidgetList), TypeKind.OBJECT, "tripleListArg", "arg1", "[[[String]!]!]!")] + [TestCase(typeof(WidgetList), TypeKind.OBJECT, "tripleListArgFixed", "arg1", "[[[String]]]")] + [TestCase(typeof(IWidgetList), TypeKind.INTERFACE, "tripleListArg", "arg1", "[[[String]!]!]!")] + [TestCase(typeof(IWidgetList), TypeKind.INTERFACE, "tripleListArgFixed", "arg1", "[[[String]]]")] + [TestCase(typeof(WidgetListController), TypeKind.CONTROLLER, "stringListArgument", "arg1", "[String]!")] + [TestCase(typeof(WidgetListController), TypeKind.CONTROLLER, "stringListArgumentFixed", "arg1", "[String]")] + public void DeclareArgumentListsAsNonNull_TypeExpressionsAreUpdated( + Type targetType, + TypeKind typeKind, + string fieldName, + string argName, + string expectedTypeExpression) + { + ExecuteStrategyTest( + targetType, + typeKind, + fieldName, + argName, + expectedTypeExpression, + x => x.DeclareArgumentListsAsNonNull(x => true)); + } + + private void ExecuteStrategyTest( + Type targetType, + TypeKind typeKind, + string fieldName, + string argName, + string expectedTypeExpression, + Action strategyToApply) + { + var builder = SchemaFormatStrategyBuilder.Create(); + strategyToApply(builder); + + var strategy = builder.Build(); + + var server = new TestServerBuilder() + .AddGraphQL(o => + { + o.AddType(targetType, typeKind); + o.DeclarationOptions.SchemaFormatStrategy = strategy; + }) + .Build(); + + if (typeKind == TypeKind.CONTROLLER) + { + var query = server.Schema.KnownTypes.FindGraphType("Query") as IObjectGraphType; + var controllerField = query.Fields[fieldName]; + + if (!string.IsNullOrWhiteSpace(argName)) + { + var controllerFieldArg = controllerField.Arguments[argName]; + Assert.AreEqual(expectedTypeExpression, controllerFieldArg.TypeExpression.ToString()); + return; + } + + Assert.AreEqual(expectedTypeExpression, controllerField.TypeExpression.ToString()); + return; + } + + var graphType = server.Schema.KnownTypes.FindGraphType(targetType, typeKind); + if (typeKind == TypeKind.INPUT_OBJECT) + { + var field = ((IInputObjectGraphType)graphType).Fields[fieldName]; + Assert.AreEqual(expectedTypeExpression, field.TypeExpression.ToString()); + return; + } + + var container = graphType as IGraphFieldContainer; + if (string.IsNullOrWhiteSpace(argName)) + { + Assert.AreEqual(expectedTypeExpression, container[fieldName].TypeExpression.ToString()); + return; + } + + var arg = container[fieldName].Arguments[argName]; + Assert.AreEqual(expectedTypeExpression, arg.TypeExpression.ToString()); + } + } +} \ No newline at end of file diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/SchemaFormatStrategy_NonNullValueTests.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/SchemaFormatStrategy_NonNullValueTests.cs new file mode 100644 index 000000000..20c131339 --- /dev/null +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/SchemaFormatStrategy_NonNullValueTests.cs @@ -0,0 +1,186 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Tests.Configuration +{ + using System; + using GraphQL.AspNet.Configuration.Formatting; + using GraphQL.AspNet.Interfaces.Schema; + using GraphQL.AspNet.Schemas.TypeSystem; + using GraphQL.AspNet.Tests.Configuration.FormatStrategyTestData; + using GraphQL.AspNet.Tests.Framework; + using NUnit.Framework; + + [TestFixture] + public class SchemaFormatStrategy_NonNullValueTests + { + [TestCase(typeof(Widget), TypeKind.INPUT_OBJECT, "stringProp", "String!")] + [TestCase(typeof(Widget), TypeKind.INPUT_OBJECT, "fixedStringProp", "String")] + public void DeclareInputFieldValuesAsNonNull_TypeExpressionsAreUpdated( + Type targetType, + TypeKind typeKind, + string fieldName, + string expectedTypeExpression) + { + ExecuteStrategyTest( + targetType, + typeKind, + fieldName, + string.Empty, + expectedTypeExpression, + x => x.DeclareInputFieldValuesAsNonNull(x => true)); + } + + [TestCase(typeof(Widget), TypeKind.OBJECT, "stringProp", "String!")] + [TestCase(typeof(Widget), TypeKind.OBJECT, "fixedStringProp", "String")] + [TestCase(typeof(IWidget), TypeKind.INTERFACE, "stringProp", "String!")] + [TestCase(typeof(IWidget), TypeKind.INTERFACE, "fixedStringProp", "String")] + [TestCase(typeof(WidgetArgumentController), TypeKind.CONTROLLER, "withArgument", "Widget!")] + [TestCase(typeof(WidgetArgumentController), TypeKind.CONTROLLER, "withArgumentFixed", "Widget")] + public void DeclareFieldValuesAsNonNull_TypeExpressionsAreUpdated( + Type targetType, + TypeKind typeKind, + string fieldName, + string expectedTypeExpression) + { + ExecuteStrategyTest( + targetType, + typeKind, + fieldName, + string.Empty, + expectedTypeExpression, + x => x.DeclareFieldValuesAsNonNull(x => true)); + } + + [TestCase(typeof(Widget), TypeKind.OBJECT, "argItem", "arg1", "String!")] + [TestCase(typeof(Widget), TypeKind.OBJECT, "fixedArgItem", "arg1", "String")] + [TestCase(typeof(IWidget), TypeKind.INTERFACE, "argItem", "arg1", "String!")] + [TestCase(typeof(IWidget), TypeKind.INTERFACE, "fixedArgItem", "arg1", "String")] + [TestCase(typeof(WidgetArgumentController), TypeKind.CONTROLLER, "withArgument", "arg1", "String!")] + [TestCase(typeof(WidgetArgumentController), TypeKind.CONTROLLER, "withArgumentFixed", "arg1", "String")] + public void DeclareArgumentValuesAsNonNull_TypeExpressionsAreUpdated( + Type targetType, + TypeKind typeKind, + string fieldName, + string argName, + string expectedTypeExpression) + { + ExecuteStrategyTest( + targetType, + typeKind, + fieldName, + argName, + expectedTypeExpression, + x => x.DeclareArgumentValuesAsNonNull(x => true)); + } + + private void ExecuteStrategyTest( + Type targetType, + TypeKind typeKind, + string fieldName, + string argName, + string expectedTypeExpression, + Action strategyToApply) + { + var builder = SchemaFormatStrategyBuilder.Create(); + strategyToApply(builder); + + var strategy = builder.Build(); + + var server = new TestServerBuilder() + .AddGraphQL(o => + { + o.AddType(targetType, typeKind); + o.DeclarationOptions.SchemaFormatStrategy = strategy; + }) + .Build(); + + if (typeKind == TypeKind.CONTROLLER) + { + var query = server.Schema.KnownTypes.FindGraphType("Query") as IObjectGraphType; + var controllerField = query.Fields[fieldName]; + + if (!string.IsNullOrWhiteSpace(argName)) + { + var controllerFieldArg = controllerField.Arguments[argName]; + Assert.AreEqual(expectedTypeExpression, controllerFieldArg.TypeExpression.ToString()); + return; + } + + Assert.AreEqual(expectedTypeExpression, controllerField.TypeExpression.ToString()); + return; + } + + var graphType = server.Schema.KnownTypes.FindGraphType(targetType, typeKind); + if (typeKind == TypeKind.INPUT_OBJECT) + { + var field = ((IInputObjectGraphType)graphType).Fields[fieldName]; + Assert.AreEqual(expectedTypeExpression, field.TypeExpression.ToString()); + return; + } + + var container = graphType as IGraphFieldContainer; + if (string.IsNullOrWhiteSpace(argName)) + { + Assert.AreEqual(expectedTypeExpression, container[fieldName].TypeExpression.ToString()); + return; + } + + var arg = container[fieldName].Arguments[argName]; + Assert.AreEqual(expectedTypeExpression, arg.TypeExpression.ToString()); + } + + [Test] + public void InputObjectWithNullableField_WithNonNullDefault_DefaultIsRetained_WhenFieldMadeNonNull() + { + var strategy = SchemaFormatStrategyBuilder.Create() + .DeclareInputFieldValuesAsNonNull(x => x.ObjectType == typeof(string)) + .Build(); + + var server = new TestServerBuilder() + .AddGraphQL(o => + { + o.AddType(TypeKind.INPUT_OBJECT); + o.DeclarationOptions.SchemaFormatStrategy = strategy; + }) + .Build(); + + var graphType = server.Schema.KnownTypes.FindGraphType($"Input_{nameof(WidgetWithDefaultValue)}") as IInputObjectGraphType; + var field = graphType.Fields["stringProp"]; + + Assert.AreEqual("String!", field.TypeExpression.ToString()); + Assert.IsTrue(field.HasDefaultValue); + Assert.AreEqual("default 1", field.DefaultValue.ToString()); + } + + [Test] + public void FieldWithNullableFieldArgument_WithNonNullDefault_DefaultIsRetained_WhenFieldMadeNonNull() + { + var strategy = SchemaFormatStrategyBuilder.Create() + .DeclareArgumentValuesAsNonNull(x => x.ObjectType == typeof(string)) + .Build(); + + var server = new TestServerBuilder() + .AddGraphQL(o => + { + o.AddController(); + o.DeclarationOptions.SchemaFormatStrategy = strategy; + }) + .Build(); + + var graphType = server.Schema.KnownTypes.FindGraphType("Query") as IGraphFieldContainer; + var field = graphType.Fields["retrieveRootWidget"]; + var arg1 = field.Arguments["arg1"]; + + Assert.AreEqual("String!", arg1.TypeExpression.ToString()); + Assert.IsTrue(arg1.HasDefaultValue); + Assert.AreEqual("default 1", arg1.DefaultValue.ToString()); + } + } +} \ No newline at end of file diff --git a/src/unit-tests/graphql-aspnet-tests/Execution/ApolloTracingTests.cs b/src/unit-tests/graphql-aspnet-tests/Execution/ApolloTracingTests.cs index 2bab0c371..62a2671ec 100644 --- a/src/unit-tests/graphql-aspnet-tests/Execution/ApolloTracingTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Execution/ApolloTracingTests.cs @@ -13,6 +13,7 @@ namespace GraphQL.AspNet.Tests.Execution using System.Linq; using System.Threading.Tasks; using GraphQL.AspNet.Common.Extensions; + using GraphQL.AspNet.Configuration.Formatting; using GraphQL.AspNet.Engine; using GraphQL.AspNet.Execution.Metrics; using GraphQL.AspNet.Schemas; @@ -91,6 +92,9 @@ public async Task StandardExcution_OutputTest() serverBuilder.AddGraphQL(o => { o.ResponseOptions.ExposeMetrics = true; + + // don't apply any formatting to intermediate objects, just name changes + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(); }); var server = serverBuilder.Build(); @@ -138,7 +142,7 @@ public async Task StandardExcution_OutputTest() ""path"": [""simple""], ""fieldName"": ""simple"", ""parentType"" : ""Query"", - ""returnType"": ""Query_Simple!"", + ""returnType"": ""Query_Simple"", ""startOffset"": [simpleStartOffset], ""duration"": [simpleDuration] }, @@ -178,7 +182,7 @@ public async Task StandardExcution_OutputTest() .Replace("[Property1Offset]", property1.StartOffsetNanoseconds.ToString()) .Replace("[Property1Duration]", property1.DurationNanoSeconds.ToString()); - CommonAssertions.AreEqualJsonStrings(expectedResult, result); + CommonAssertions.AreEqualJsonStrings(expectedResult, result, "Results did not match"); } [Test] @@ -202,6 +206,7 @@ public async Task Tracing_ThroughBatchTypeExtension_WithSingleObjectPerSourceRes serverBuilder.AddGraphQL(o => { o.ResponseOptions.ExposeMetrics = true; + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(); }); var batchService = Substitute.For(); @@ -258,7 +263,7 @@ public async Task Tracing_ThroughBatchTypeExtension_WithSingleObjectPerSourceRes ""path"": [ ""batch""], ""parentType"": ""Query"", ""fieldName"": ""batch"", - ""returnType"": ""Query_Batch!"", + ""returnType"": ""Query_Batch"", ""startOffset"": """", ""duration"": """" }, @@ -346,6 +351,7 @@ public async Task Tracing_ThroughBatchTypeExtension_WithMultiObjectPerSourceResu serverBuilder.AddGraphQL(o => { o.ResponseOptions.ExposeMetrics = true; + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(); }); var batchService = Substitute.For(); @@ -429,7 +435,7 @@ public async Task Tracing_ThroughBatchTypeExtension_WithMultiObjectPerSourceResu ""path"": [""batch""], ""parentType"": ""Query"", ""fieldName"": ""batch"", - ""returnType"": ""Query_Batch!"", + ""returnType"": ""Query_Batch"", ""startOffset"": """", ""duration"": """" }, diff --git a/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/EnumGraphTypeMakerTests.cs b/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/EnumGraphTypeMakerTests.cs index aa4aed68c..69139cb2b 100644 --- a/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/EnumGraphTypeMakerTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/EnumGraphTypeMakerTests.cs @@ -87,7 +87,7 @@ public void CreateGraphType_ParsesAsExpected() .AddGraphQL(o => { o.DeclarationOptions.SchemaFormatStrategy = - new SchemaFormatStrategy(enumValueStrategy: SchemaItemNameFormatOptions.NoChanges); + SchemaFormatStrategy.CreateEmpty(enumValueNameFormat: TextFormatOptions.NoChanges); }) .Build() .Schema; @@ -174,7 +174,7 @@ public void EnumValueIsKeyword_AndFormattingMatchesKeyword_ThrowsException(Type { var schema = new TestServerBuilder().AddGraphQL(o => { - o.DeclarationOptions.SchemaFormatStrategy = new SchemaFormatStrategy(SchemaItemNameFormatOptions.LowerCase); + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(TextFormatOptions.LowerCase); }) .Build() .Schema; diff --git a/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/ScalarGraphTypeMakerTests.cs b/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/ScalarGraphTypeMakerTests.cs index e8ccfafbc..6a20b5410 100644 --- a/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/ScalarGraphTypeMakerTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/ScalarGraphTypeMakerTests.cs @@ -42,36 +42,36 @@ public void NullType_ReturnsNullResult() } // fixed name scalars will never be renamed - [TestCase(typeof(int), SchemaItemNameFormatOptions.UpperCase, "Int")] - [TestCase(typeof(int), SchemaItemNameFormatOptions.LowerCase, "Int")] - [TestCase(typeof(int), SchemaItemNameFormatOptions.ProperCase, "Int")] - [TestCase(typeof(float), SchemaItemNameFormatOptions.UpperCase, "Float")] - [TestCase(typeof(float), SchemaItemNameFormatOptions.LowerCase, "Float")] - [TestCase(typeof(float), SchemaItemNameFormatOptions.ProperCase, "Float")] - [TestCase(typeof(string), SchemaItemNameFormatOptions.UpperCase, "String")] - [TestCase(typeof(string), SchemaItemNameFormatOptions.LowerCase, "String")] - [TestCase(typeof(string), SchemaItemNameFormatOptions.ProperCase, "String")] - [TestCase(typeof(bool), SchemaItemNameFormatOptions.UpperCase, "Boolean")] - [TestCase(typeof(bool), SchemaItemNameFormatOptions.LowerCase, "Boolean")] - [TestCase(typeof(bool), SchemaItemNameFormatOptions.ProperCase, "Boolean")] - [TestCase(typeof(GraphId), SchemaItemNameFormatOptions.UpperCase, "ID")] - [TestCase(typeof(GraphId), SchemaItemNameFormatOptions.LowerCase, "ID")] - [TestCase(typeof(GraphId), SchemaItemNameFormatOptions.ProperCase, "ID")] + [TestCase(typeof(int), TextFormatOptions.UpperCase, "Int")] + [TestCase(typeof(int), TextFormatOptions.LowerCase, "Int")] + [TestCase(typeof(int), TextFormatOptions.ProperCase, "Int")] + [TestCase(typeof(float), TextFormatOptions.UpperCase, "Float")] + [TestCase(typeof(float), TextFormatOptions.LowerCase, "Float")] + [TestCase(typeof(float), TextFormatOptions.ProperCase, "Float")] + [TestCase(typeof(string), TextFormatOptions.UpperCase, "String")] + [TestCase(typeof(string), TextFormatOptions.LowerCase, "String")] + [TestCase(typeof(string), TextFormatOptions.ProperCase, "String")] + [TestCase(typeof(bool), TextFormatOptions.UpperCase, "Boolean")] + [TestCase(typeof(bool), TextFormatOptions.LowerCase, "Boolean")] + [TestCase(typeof(bool), TextFormatOptions.ProperCase, "Boolean")] + [TestCase(typeof(GraphId), TextFormatOptions.UpperCase, "ID")] + [TestCase(typeof(GraphId), TextFormatOptions.LowerCase, "ID")] + [TestCase(typeof(GraphId), TextFormatOptions.ProperCase, "ID")] // non-fixed scalars will rename themselves - [TestCase(typeof(decimal), SchemaItemNameFormatOptions.UpperCase, "DECIMAL")] - [TestCase(typeof(decimal), SchemaItemNameFormatOptions.LowerCase, "decimal")] - [TestCase(typeof(decimal), SchemaItemNameFormatOptions.ProperCase, "Decimal")] - [TestCase(typeof(Uri), SchemaItemNameFormatOptions.UpperCase, "URI")] - [TestCase(typeof(Uri), SchemaItemNameFormatOptions.LowerCase, "uri")] - [TestCase(typeof(Uri), SchemaItemNameFormatOptions.ProperCase, "Uri")] - public void BuiltInScalar_ObeysNamingRulesOfConfig(Type builtInScalarType, SchemaItemNameFormatOptions strategy, string expectedName) + [TestCase(typeof(decimal), TextFormatOptions.UpperCase, "DECIMAL")] + [TestCase(typeof(decimal), TextFormatOptions.LowerCase, "decimal")] + [TestCase(typeof(decimal), TextFormatOptions.ProperCase, "Decimal")] + [TestCase(typeof(Uri), TextFormatOptions.UpperCase, "URI")] + [TestCase(typeof(Uri), TextFormatOptions.LowerCase, "uri")] + [TestCase(typeof(Uri), TextFormatOptions.ProperCase, "Uri")] + public void BuiltInScalar_ObeysNamingRulesOfConfig(Type builtInScalarType, TextFormatOptions nameFormat, string expectedName) { var server = new TestServerBuilder() .AddGraphQL(o => { o.DeclarationOptions.SchemaFormatStrategy - = new SchemaFormatStrategy(strategy); + = SchemaFormatStrategy.CreateEmpty(nameFormat); }) .Build(); From d83d169835c7e8df3fdf30cab6fc5f8490202d6a Mon Sep 17 00:00:00 2001 From: Kevin Carroll Date: Sat, 25 May 2024 22:10:01 -0700 Subject: [PATCH 5/6] WIP, variable name and schema strategy builder clean up --- .../Attributes/SubscriptionRootAttribute.cs | 7 +- .../Attributes/MutationAttribute.cs | 7 +- .../Attributes/MutationRootAttribute.cs | 7 +- .../Attributes/QueryAttribute.cs | 7 +- .../Attributes/QueryRootAttribute.cs | 7 +- .../Formatting/SchemaFormatStrategy.cs | 45 ---- .../Formatting/SchemaFormatStrategyBuilder.cs | 211 +++++++++++++----- .../SchemaDeclarationConfiguration.cs | 3 +- .../GraphQLSchemaHelperMethods.cs | 4 +- .../TestServerBuilder{TSchema}.cs | 6 +- .../ConfigurationFieldNamingTests.cs | 12 +- .../Execution/ApolloTracingTests.cs | 12 +- .../TypeMakers/EnumGraphTypeMakerTests.cs | 8 +- .../TypeMakers/ScalarGraphTypeMakerTests.cs | 5 +- 14 files changed, 208 insertions(+), 133 deletions(-) diff --git a/src/graphql-aspnet-subscriptions/Attributes/SubscriptionRootAttribute.cs b/src/graphql-aspnet-subscriptions/Attributes/SubscriptionRootAttribute.cs index 22ba93eb7..ac9db7af1 100644 --- a/src/graphql-aspnet-subscriptions/Attributes/SubscriptionRootAttribute.cs +++ b/src/graphql-aspnet-subscriptions/Attributes/SubscriptionRootAttribute.cs @@ -11,7 +11,6 @@ namespace GraphQL.AspNet.Attributes { using System; using System.Linq; - using GraphQL.AspNet.Common.Extensions; using GraphQL.AspNet.Execution; using GraphQL.AspNet.Interfaces.Schema; @@ -92,15 +91,15 @@ public SubscriptionRootAttribute(string template, Type returnType, params Type[] /// /// The template naming scheme to use to generate a graph field from this method. /// Name of the union type. - /// The first of two required types to include in the union. + /// The first of two required types to include in the union. /// Any additional union types. - public SubscriptionRootAttribute(string template, string unionTypeName, Type unionTypeA, params Type[] additionalUnionTypes) + public SubscriptionRootAttribute(string template, string unionTypeName, Type firstUnionType, params Type[] additionalUnionTypes) : base( true, ItemPathRoots.Subscription, template, unionTypeName, - (new Type[] { unionTypeA }).Concat(additionalUnionTypes ?? Enumerable.Empty()).ToArray()) + (new Type[] { firstUnionType }).Concat(additionalUnionTypes ?? Enumerable.Empty()).ToArray()) { this.EventName = null; } diff --git a/src/graphql-aspnet/Attributes/MutationAttribute.cs b/src/graphql-aspnet/Attributes/MutationAttribute.cs index 13d4ca225..426d24d83 100644 --- a/src/graphql-aspnet/Attributes/MutationAttribute.cs +++ b/src/graphql-aspnet/Attributes/MutationAttribute.cs @@ -11,7 +11,6 @@ namespace GraphQL.AspNet.Attributes { using System; using System.Linq; - using GraphQL.AspNet.Common.Extensions; using GraphQL.AspNet.Execution; using GraphQL.AspNet.Interfaces.Schema; @@ -101,15 +100,15 @@ public MutationAttribute(string template, Type returnType, params Type[] additio /// /// The template naming scheme to use to generate a graph field from this method. /// Name of the union type. - /// The first of two required types to include in the union. + /// The first of two required types to include in the union. /// Any additional union types. - public MutationAttribute(string template, string unionTypeName, Type unionTypeA, params Type[] additionalUnionTypes) + public MutationAttribute(string template, string unionTypeName, Type firstUnionType, params Type[] additionalUnionTypes) : base( false, ItemPathRoots.Mutation, template, unionTypeName, - (new Type[] { unionTypeA }).Concat(additionalUnionTypes ?? Enumerable.Empty()).ToArray()) + (new Type[] { firstUnionType }).Concat(additionalUnionTypes ?? Enumerable.Empty()).ToArray()) { } } diff --git a/src/graphql-aspnet/Attributes/MutationRootAttribute.cs b/src/graphql-aspnet/Attributes/MutationRootAttribute.cs index c4b80d6aa..f57189d6a 100644 --- a/src/graphql-aspnet/Attributes/MutationRootAttribute.cs +++ b/src/graphql-aspnet/Attributes/MutationRootAttribute.cs @@ -11,7 +11,6 @@ namespace GraphQL.AspNet.Attributes { using System; using System.Linq; - using GraphQL.AspNet.Common.Extensions; using GraphQL.AspNet.Execution; using GraphQL.AspNet.Interfaces.Schema; @@ -100,15 +99,15 @@ public MutationRootAttribute(string template, Type returnType, params Type[] add /// /// The template naming scheme to use to generate a graph field from this method. /// Name of the union type. - /// The first of two required types to include in the union. + /// The first of two required types to include in the union. /// Any additional union types. - public MutationRootAttribute(string template, string unionTypeName, Type unionTypeA, params Type[] additionalUnionTypes) + public MutationRootAttribute(string template, string unionTypeName, Type firstUnionType, params Type[] additionalUnionTypes) : base( true, ItemPathRoots.Mutation, template, unionTypeName, - (new Type[] { unionTypeA }).Concat(additionalUnionTypes ?? Enumerable.Empty()).ToArray()) + (new Type[] { firstUnionType }).Concat(additionalUnionTypes ?? Enumerable.Empty()).ToArray()) { } } diff --git a/src/graphql-aspnet/Attributes/QueryAttribute.cs b/src/graphql-aspnet/Attributes/QueryAttribute.cs index 048dbe35a..e56bb3cbc 100644 --- a/src/graphql-aspnet/Attributes/QueryAttribute.cs +++ b/src/graphql-aspnet/Attributes/QueryAttribute.cs @@ -11,7 +11,6 @@ namespace GraphQL.AspNet.Attributes { using System; using System.Linq; - using GraphQL.AspNet.Common.Extensions; using GraphQL.AspNet.Execution; using GraphQL.AspNet.Interfaces.Schema; @@ -101,15 +100,15 @@ public QueryAttribute(string template, Type returnType, params Type[] additional /// /// The template naming scheme to use to generate a graph field from this method. /// Name of the union type. - /// The first type to include in the union. + /// The first type to include in the union. /// Any additional union types to include. - public QueryAttribute(string template, string unionTypeName, Type unionTypeA, params Type[] additionalUnionTypes) + public QueryAttribute(string template, string unionTypeName, Type firstUnionType, params Type[] additionalUnionTypes) : base( false, ItemPathRoots.Query, template, unionTypeName, - (new Type[] { unionTypeA }).Concat(additionalUnionTypes ?? Enumerable.Empty()).ToArray()) + (new Type[] { firstUnionType }).Concat(additionalUnionTypes ?? Enumerable.Empty()).ToArray()) { } } diff --git a/src/graphql-aspnet/Attributes/QueryRootAttribute.cs b/src/graphql-aspnet/Attributes/QueryRootAttribute.cs index 8dd9e6c26..3ac6a3e3f 100644 --- a/src/graphql-aspnet/Attributes/QueryRootAttribute.cs +++ b/src/graphql-aspnet/Attributes/QueryRootAttribute.cs @@ -11,7 +11,6 @@ namespace GraphQL.AspNet.Attributes { using System; using System.Linq; - using GraphQL.AspNet.Common.Extensions; using GraphQL.AspNet.Execution; using GraphQL.AspNet.Interfaces.Schema; @@ -104,15 +103,15 @@ public QueryRootAttribute(string template, Type returnType, params Type[] additi /// /// The template naming scheme to use to generate a graph field from this method. /// Name of the union type. - /// The first of two required types to include in the union. + /// The first of two required types to include in the union. /// Any additional union types. - public QueryRootAttribute(string template, string unionTypeName, Type unionTypeA, params Type[] additionalUnionTypes) + public QueryRootAttribute(string template, string unionTypeName, Type firstUnionType, params Type[] additionalUnionTypes) : base( true, ItemPathRoots.Query, template, unionTypeName, - (new Type[] { unionTypeA }).Concat(additionalUnionTypes ?? Enumerable.Empty()).ToArray()) + (new Type[] { firstUnionType }).Concat(additionalUnionTypes ?? Enumerable.Empty()).ToArray()) { } } diff --git a/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs index be36de663..ab448a17b 100644 --- a/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs +++ b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs @@ -20,51 +20,6 @@ namespace GraphQL.AspNet.Configuration.Formatting /// public class SchemaFormatStrategy : ISchemaFormatStrategy { - /// - /// Creates a format strategy with all internal default rules. All name format rules - /// will be overriden with the given format option. - /// - /// The single format option to use for all generated names. - /// SchemaFormatStrategy. - public static ISchemaFormatStrategy CreateEmpty(TextFormatOptions formatOption) - { - return CreateEmpty(formatOption, formatOption, formatOption, formatOption, formatOption); - } - - /// - /// Creates a format strategy with all internal default rules. Name format overrides - /// can be applied if needed. - /// - /// The name format to use for graph type names. - /// The name format to use for fields. - /// The name format to use for enum values. - /// The name format to use for arguments on fields and directives. - /// The name format to use for directive type names. - /// SchemaFormatStrategy. - public static ISchemaFormatStrategy CreateEmpty( - TextFormatOptions? typeNameFormat = TextFormatOptions.ProperCase, - TextFormatOptions? fieldNameFormat = TextFormatOptions.CamelCase, - TextFormatOptions? enumValueNameFormat = TextFormatOptions.UpperCase, - TextFormatOptions? argumentNameFormat = TextFormatOptions.CamelCase, - TextFormatOptions? directiveNameFormat = TextFormatOptions.CamelCase) - { - var builder = new SchemaFormatStrategyBuilder() - .Clear(); - - if (typeNameFormat.HasValue) - builder = builder.WithGraphTypeNameFormat(typeNameFormat.Value); - if (fieldNameFormat.HasValue) - builder = builder.WithFieldNameFormat(fieldNameFormat.Value); - if (enumValueNameFormat.HasValue) - builder = builder.WithEnumValueFormat(enumValueNameFormat.Value); - if (directiveNameFormat.HasValue) - builder = builder.WithDirectiveNameFormat(directiveNameFormat.Value); - if (argumentNameFormat.HasValue) - builder = builder.WithFieldArgumentNameFormat(argumentNameFormat.Value); - - return builder.Build(); - } - private List _formatRules; /// diff --git a/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategyBuilder.cs b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategyBuilder.cs index 244371596..f65698e30 100644 --- a/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategyBuilder.cs +++ b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategyBuilder.cs @@ -24,76 +24,117 @@ namespace GraphQL.AspNet.Configuration.Formatting public class SchemaFormatStrategyBuilder { /// - /// Starts a new strategy builder instance. + /// Creates a format strategy builder and applies the given format option for all name format rules. /// - /// SchemaFormatStrategyBuilder. - public static SchemaFormatStrategyBuilder Create() + /// The single format option to use for all generated names. + /// if set to true default format rules + /// are applied to the builder. When false, no rules are applied and the builder is created + /// in an empty state. + /// SchemaFormatStrategy. + public static SchemaFormatStrategyBuilder Create( + TextFormatOptions formatOption, + bool applyDefaultRules = true) { - return new SchemaFormatStrategyBuilder(); + return CreateInternal( + false, + formatOption, + formatOption, + formatOption, + formatOption, + formatOption); } - private TextFormatOptions? _typeNameFormat; - private TextFormatOptions? _fieldNameFormat; - private TextFormatOptions? _enumValueFormat; - private TextFormatOptions? _argumentNameFormat; - private TextFormatOptions? _directiveNameFormat; - private List _customRules; - /// - /// Initializes a new instance of the class. + /// Creates a format strategy builder with the provided text format overrides + /// will be applied as defined. /// - public SchemaFormatStrategyBuilder() + /// The name format to use for graph type names. + /// The name format to use for fields. + /// The name format to use for enum values. + /// The name format to use for arguments on fields and directives. + /// The name format to use for directive type names. + /// if set to true default format rules + /// are applied to the builder. When false, no rules are applied and the builder is created + /// in an empty state. + /// SchemaFormatStrategy. + public static SchemaFormatStrategyBuilder Create( + TextFormatOptions? typeNameFormat = TextFormatOptions.ProperCase, + TextFormatOptions? fieldNameFormat = TextFormatOptions.CamelCase, + TextFormatOptions? enumValueNameFormat = TextFormatOptions.UpperCase, + TextFormatOptions? argumentNameFormat = TextFormatOptions.CamelCase, + TextFormatOptions? directiveNameFormat = TextFormatOptions.CamelCase, + bool applyDefaultRules = true) { - _customRules = new List(); + return CreateInternal( + applyDefaultRules, + typeNameFormat, + fieldNameFormat, + enumValueNameFormat, + argumentNameFormat, + directiveNameFormat); + } - _typeNameFormat = TextFormatOptions.ProperCase; - _enumValueFormat = TextFormatOptions.UpperCase; + private static SchemaFormatStrategyBuilder CreateInternal( + bool applyDefaultRules, + TextFormatOptions? typeNameFormat, + TextFormatOptions? fieldNameFormat, + TextFormatOptions? enumValueNameFormat, + TextFormatOptions? argumentNameFormat, + TextFormatOptions? directiveNameFormat) + { + var builder = new SchemaFormatStrategyBuilder(); - _fieldNameFormat = TextFormatOptions.CamelCase; - _argumentNameFormat = TextFormatOptions.CamelCase; - _directiveNameFormat = TextFormatOptions.CamelCase; + if (!applyDefaultRules) + builder = builder.Clear(); - // set all aspects of virtual fields as "non null" by default - _customRules.Add(new ApplyFieldNonNullItemTypeExpressionFormatRule(x => x.IsVirtual)); - _customRules.Add(new ApplyFieldNonNullListTypeExpressionFormatRule(x => x.IsVirtual)); + if (typeNameFormat.HasValue) + builder = builder.WithGraphTypeNameFormat(typeNameFormat.Value); + if (fieldNameFormat.HasValue) + builder = builder.WithFieldNameFormat(fieldNameFormat.Value); + if (enumValueNameFormat.HasValue) + builder = builder.WithEnumValueFormat(enumValueNameFormat.Value); + if (directiveNameFormat.HasValue) + builder = builder.WithDirectiveNameFormat(directiveNameFormat.Value); + if (argumentNameFormat.HasValue) + builder = builder.WithFieldArgumentNameFormat(argumentNameFormat.Value); + + return builder; } + private TextFormatOptions? _typeNameFormat = null; + private TextFormatOptions? _fieldNameFormat = null; + private TextFormatOptions? _enumValueFormat = null; + private TextFormatOptions? _argumentNameFormat = null; + private TextFormatOptions? _directiveNameFormat = null; + private List _customRules = null; + + private bool _includeIntermediateTypeNonNullRules; + /// - /// Creates a set of rules to apply the name formats requested on this builder. + /// Initializes a new instance of the class. /// - /// IEnumerable<ISchemaItemFormatRule>. - protected virtual IEnumerable CreateNameFormatRules() + protected SchemaFormatStrategyBuilder() { - // formal name formatting - if (_typeNameFormat.HasValue) - { - yield return new ApplyGraphTypeNameFormatRule(_typeNameFormat.Value); - yield return new ApplyGraphTypeNameFormatToUnionTypeMembersRule(_typeNameFormat.Value); - yield return new ApplyFieldTypeExpressionNameFormatRule(_typeNameFormat.Value); - yield return new ApplyArgumentTypeExpressionNameFormatRule(_typeNameFormat.Value); - yield return new ApplyInputFieldTypeExpressionNameFormatRule(_typeNameFormat.Value); - } - - if (_fieldNameFormat.HasValue) - { - yield return new ApplyFieldNameFormatRule(_fieldNameFormat.Value); - yield return new ApplyInputFieldNameFormatRule(_fieldNameFormat.Value); - } - - if (_directiveNameFormat.HasValue) - { - yield return new ApplyDirectiveNameFormatRule(_directiveNameFormat.Value); - } + _customRules = new List(); - if (_argumentNameFormat.HasValue) - { - yield return new ApplyArgumentNameFormatRule(_argumentNameFormat.Value); - } + // set all aspects of virtual fields as "non null" by default + _includeIntermediateTypeNonNullRules = true; + } - if (_enumValueFormat.HasValue) - { - yield return new ApplyEnumNameFormatRule(_enumValueFormat.Value); - } + /// + /// Sets a rule such that intermediate graph type fields are declared as "non null". + /// These are guaranteed to exist by the runtime to always be present and this declaration is made + /// by default. + /// + /// + /// Use the method to unset this rule. This can be useful for backwards + /// compatiability with v1.x of this library. + /// + /// SchemaFormatStrategyBuilder. + public virtual SchemaFormatStrategyBuilder DeclareIntermediateTypesAsNonNull() + { + _includeIntermediateTypeNonNullRules = true; + return this; } /// @@ -272,7 +313,7 @@ public virtual SchemaFormatStrategyBuilder WithDirectiveNameFormat(TextFormatOpt /// Clears all custom rules and name format options. /// /// SchemaFormatStrategyBuilder. - public SchemaFormatStrategyBuilder Clear() + public virtual SchemaFormatStrategyBuilder Clear() { _customRules.Clear(); @@ -282,6 +323,8 @@ public SchemaFormatStrategyBuilder Clear() _argumentNameFormat = null; _directiveNameFormat = null; + _includeIntermediateTypeNonNullRules = false; + return this; } @@ -290,12 +333,70 @@ public SchemaFormatStrategyBuilder Clear() /// /// ISchemaFormatStrategy. public virtual ISchemaFormatStrategy Build() + { + return new SchemaFormatStrategy(this.GatherRules()); + } + + /// + /// Gathers the rules set on this builder into a single array. + /// + /// ISchemaItemFormatRule[]. + protected virtual ISchemaItemFormatRule[] GatherRules() { var ruleSet = new List(); + + // name rules first ruleSet.AddRange(this.CreateNameFormatRules()); + + // intermediate type rules second + if (_includeIntermediateTypeNonNullRules) + { + ruleSet.Add(new ApplyFieldNonNullItemTypeExpressionFormatRule(x => x.IsVirtual)); + ruleSet.Add(new ApplyFieldNonNullListTypeExpressionFormatRule(x => x.IsVirtual)); + } + + // used defined rules last ruleSet.AddRange(_customRules); - return new SchemaFormatStrategy(ruleSet.ToArray()); + return ruleSet.ToArray(); + } + + /// + /// Creates a set of rules to apply the name formats requested on this builder. + /// + /// IEnumerable<ISchemaItemFormatRule>. + protected virtual IEnumerable CreateNameFormatRules() + { + // formal name formatting + if (_typeNameFormat.HasValue) + { + yield return new ApplyGraphTypeNameFormatRule(_typeNameFormat.Value); + yield return new ApplyGraphTypeNameFormatToUnionTypeMembersRule(_typeNameFormat.Value); + yield return new ApplyFieldTypeExpressionNameFormatRule(_typeNameFormat.Value); + yield return new ApplyArgumentTypeExpressionNameFormatRule(_typeNameFormat.Value); + yield return new ApplyInputFieldTypeExpressionNameFormatRule(_typeNameFormat.Value); + } + + if (_fieldNameFormat.HasValue) + { + yield return new ApplyFieldNameFormatRule(_fieldNameFormat.Value); + yield return new ApplyInputFieldNameFormatRule(_fieldNameFormat.Value); + } + + if (_directiveNameFormat.HasValue) + { + yield return new ApplyDirectiveNameFormatRule(_directiveNameFormat.Value); + } + + if (_argumentNameFormat.HasValue) + { + yield return new ApplyArgumentNameFormatRule(_argumentNameFormat.Value); + } + + if (_enumValueFormat.HasValue) + { + yield return new ApplyEnumNameFormatRule(_enumValueFormat.Value); + } } } } \ No newline at end of file diff --git a/src/graphql-aspnet/Configuration/SchemaDeclarationConfiguration.cs b/src/graphql-aspnet/Configuration/SchemaDeclarationConfiguration.cs index 212cd9993..3d0df73da 100644 --- a/src/graphql-aspnet/Configuration/SchemaDeclarationConfiguration.cs +++ b/src/graphql-aspnet/Configuration/SchemaDeclarationConfiguration.cs @@ -11,6 +11,7 @@ namespace GraphQL.AspNet.Configuration { using System.Collections.Generic; using System.Diagnostics; + using GraphQL.AspNet.Configuration.Formatting; using GraphQL.AspNet.Interfaces.Configuration; using GraphQL.AspNet.Interfaces.Schema; using GraphQL.AspNet.Schemas.TypeSystem; @@ -31,7 +32,7 @@ public SchemaDeclarationConfiguration() this.AllowedOperations.Add(GraphOperationType.Query); this.AllowedOperations.Add(GraphOperationType.Mutation); this.ArgumentBindingRule = SchemaArgumentBindingRules.ParametersPreferQueryResolution; - this.SchemaFormatStrategy = Formatting.SchemaFormatStrategy.CreateEmpty(); + this.SchemaFormatStrategy = SchemaFormatStrategyBuilder.Create().Build(); } /// diff --git a/src/unit-tests/graphql-aspnet-testframework/GraphQLSchemaHelperMethods.cs b/src/unit-tests/graphql-aspnet-testframework/GraphQLSchemaHelperMethods.cs index 1129ef851..b13de74ce 100644 --- a/src/unit-tests/graphql-aspnet-testframework/GraphQLSchemaHelperMethods.cs +++ b/src/unit-tests/graphql-aspnet-testframework/GraphQLSchemaHelperMethods.cs @@ -28,7 +28,9 @@ public static void SetNoAlterationConfiguration(this ISchema schema) { var declarationOptions = new SchemaDeclarationConfiguration(); declarationOptions.Merge(schema.Configuration.DeclarationOptions); - declarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(TextFormatOptions.NoChanges); + declarationOptions.SchemaFormatStrategy = SchemaFormatStrategyBuilder + .Create(TextFormatOptions.NoChanges, applyDefaultRules: false) + .Build(); var config = new SchemaConfiguration( declarationOptions, diff --git a/src/unit-tests/graphql-aspnet-testframework/TestServerBuilder{TSchema}.cs b/src/unit-tests/graphql-aspnet-testframework/TestServerBuilder{TSchema}.cs index ce67f1960..4215c04de 100644 --- a/src/unit-tests/graphql-aspnet-testframework/TestServerBuilder{TSchema}.cs +++ b/src/unit-tests/graphql-aspnet-testframework/TestServerBuilder{TSchema}.cs @@ -105,7 +105,11 @@ protected virtual void PerformInitialConfiguration(SchemaOptions options) if (_initialSetup.HasFlag(TestOptions.UseCodeDeclaredNames)) { - options.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(TextFormatOptions.NoChanges); + // use create empty here to be backwards compatiable with + // the original implementation of intermeidate types from v1x + options.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategyBuilder + .Create(TextFormatOptions.NoChanges, applyDefaultRules: false) + .Build(); } if (_initialSetup.HasFlag(TestOptions.IncludeExceptions)) diff --git a/src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationFieldNamingTests.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationFieldNamingTests.cs index 2c2673b2d..15f537f2c 100644 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationFieldNamingTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationFieldNamingTests.cs @@ -52,7 +52,9 @@ public async Task ProductionDefaults_UpperCaseFieldNames() builder.AddGraphQL(o => { - o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(fieldNameFormat: TextFormatOptions.UpperCase); + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategyBuilder + .Create(fieldNameFormat: TextFormatOptions.UpperCase, applyDefaultRules: false) + .Build(); }); var server = builder.Build(); @@ -84,7 +86,9 @@ public async Task ProductionDefaults_LowerCaseEnumValues() builder.AddGraphQL(o => { - o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(enumValueNameFormat: TextFormatOptions.LowerCase); + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategyBuilder + .Create(enumValueNameFormat: TextFormatOptions.LowerCase, applyDefaultRules: false) + .Build(); }); var server = builder.Build(); @@ -116,7 +120,9 @@ public async Task ProductionDefaults_CamelCasedTypeNames() builder.AddGraphQL(o => { - o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(typeNameFormat: TextFormatOptions.CamelCase); + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategyBuilder + .Create(typeNameFormat: TextFormatOptions.CamelCase, applyDefaultRules: false) + .Build(); }); var server = builder.Build(); diff --git a/src/unit-tests/graphql-aspnet-tests/Execution/ApolloTracingTests.cs b/src/unit-tests/graphql-aspnet-tests/Execution/ApolloTracingTests.cs index 62a2671ec..d7350dc57 100644 --- a/src/unit-tests/graphql-aspnet-tests/Execution/ApolloTracingTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Execution/ApolloTracingTests.cs @@ -94,7 +94,9 @@ public async Task StandardExcution_OutputTest() o.ResponseOptions.ExposeMetrics = true; // don't apply any formatting to intermediate objects, just name changes - o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(); + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategyBuilder + .Create(applyDefaultRules: false) + .Build(); }); var server = serverBuilder.Build(); @@ -206,7 +208,9 @@ public async Task Tracing_ThroughBatchTypeExtension_WithSingleObjectPerSourceRes serverBuilder.AddGraphQL(o => { o.ResponseOptions.ExposeMetrics = true; - o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(); + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategyBuilder + .Create(applyDefaultRules: false) + .Build(); }); var batchService = Substitute.For(); @@ -351,7 +355,9 @@ public async Task Tracing_ThroughBatchTypeExtension_WithMultiObjectPerSourceResu serverBuilder.AddGraphQL(o => { o.ResponseOptions.ExposeMetrics = true; - o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(); + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategyBuilder + .Create(applyDefaultRules: false) + .Build(); }); var batchService = Substitute.For(); diff --git a/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/EnumGraphTypeMakerTests.cs b/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/EnumGraphTypeMakerTests.cs index 69139cb2b..4ba7b1350 100644 --- a/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/EnumGraphTypeMakerTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/EnumGraphTypeMakerTests.cs @@ -87,7 +87,9 @@ public void CreateGraphType_ParsesAsExpected() .AddGraphQL(o => { o.DeclarationOptions.SchemaFormatStrategy = - SchemaFormatStrategy.CreateEmpty(enumValueNameFormat: TextFormatOptions.NoChanges); + SchemaFormatStrategyBuilder + .Create(enumValueNameFormat: TextFormatOptions.NoChanges, applyDefaultRules: false) + .Build(); }) .Build() .Schema; @@ -174,7 +176,9 @@ public void EnumValueIsKeyword_AndFormattingMatchesKeyword_ThrowsException(Type { var schema = new TestServerBuilder().AddGraphQL(o => { - o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategy.CreateEmpty(TextFormatOptions.LowerCase); + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategyBuilder + .Create(TextFormatOptions.LowerCase, applyDefaultRules: false) + .Build(); }) .Build() .Schema; diff --git a/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/ScalarGraphTypeMakerTests.cs b/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/ScalarGraphTypeMakerTests.cs index 6a20b5410..55ce9772e 100644 --- a/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/ScalarGraphTypeMakerTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Schemas/Generation/TypeMakers/ScalarGraphTypeMakerTests.cs @@ -70,8 +70,9 @@ public void BuiltInScalar_ObeysNamingRulesOfConfig(Type builtInScalarType, TextF var server = new TestServerBuilder() .AddGraphQL(o => { - o.DeclarationOptions.SchemaFormatStrategy - = SchemaFormatStrategy.CreateEmpty(nameFormat); + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategyBuilder + .Create(nameFormat, applyDefaultRules: false) + .Build(); }) .Build(); From c97b734a026148c1de0285562942cfca5739152a Mon Sep 17 00:00:00 2001 From: Kevin Carroll Date: Mon, 27 May 2024 13:10:29 -0700 Subject: [PATCH 6/6] WIP, tests and inline documentation updates --- ...hQLRuntimeSchemaItemDefinitionExtensions_Mutations.cs | 2 +- .../Configuration/SchemaItemFilterExtensions.cs | 4 ++-- .../Controllers/RuntimeFieldExecutionController.cs | 3 ++- src/graphql-aspnet/Interfaces/Schema/IEnumValue.cs | 9 +++++++-- .../RuntimeControllerActionDefinitionBase.cs | 3 ++- .../Schemas/GraphFieldCloningTests.cs | 2 -- .../Schemas/GraphTypeExpressionTests.cs | 6 ++++-- 7 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/graphql-aspnet/Configuration/GraphQLRuntimeSchemaItemDefinitionExtensions_Mutations.cs b/src/graphql-aspnet/Configuration/GraphQLRuntimeSchemaItemDefinitionExtensions_Mutations.cs index 8071e9dc0..6207a9162 100644 --- a/src/graphql-aspnet/Configuration/GraphQLRuntimeSchemaItemDefinitionExtensions_Mutations.cs +++ b/src/graphql-aspnet/Configuration/GraphQLRuntimeSchemaItemDefinitionExtensions_Mutations.cs @@ -19,7 +19,7 @@ namespace GraphQL.AspNet.Configuration using GraphQL.AspNet.Schemas.TypeSystem; /// - /// Extension methods for configuring minimal API methods as fields on the graph. + /// Extension methods for configuring minimal API methods as fields on the schema. /// public static partial class GraphQLRuntimeSchemaItemDefinitionExtensions { diff --git a/src/graphql-aspnet/Configuration/SchemaItemFilterExtensions.cs b/src/graphql-aspnet/Configuration/SchemaItemFilterExtensions.cs index 4cccdf188..04769bd6f 100644 --- a/src/graphql-aspnet/Configuration/SchemaItemFilterExtensions.cs +++ b/src/graphql-aspnet/Configuration/SchemaItemFilterExtensions.cs @@ -34,7 +34,7 @@ public static bool IsIntrospectionItem(this ISchemaItem item) /// (i.e. an item that starts with '__'). /// /// The item to inspect. - /// true if the specified item is system level data; otherwise, false. + /// true if the specified item an internal system level item; otherwise, false. public static bool IsSystemItem(this ISchemaItem item) { // name regex matches on valid "user supplied names" any schema items @@ -83,7 +83,7 @@ public static bool IsField( } /// - /// Determines whether the given schema item is a field on an OBJECT or INPUT_OBJECT graph type. + /// Determines whether the given schema item is a field on an OBJECT, INTERFACE or INPUT_OBJECT graph type. /// /// The schema item to inspect. /// the name of the graph type that owns the field. diff --git a/src/graphql-aspnet/Controllers/RuntimeFieldExecutionController.cs b/src/graphql-aspnet/Controllers/RuntimeFieldExecutionController.cs index 0140c97f7..d56d6d0e7 100644 --- a/src/graphql-aspnet/Controllers/RuntimeFieldExecutionController.cs +++ b/src/graphql-aspnet/Controllers/RuntimeFieldExecutionController.cs @@ -13,7 +13,8 @@ namespace GraphQL.AspNet.Controllers /// /// A special controller instance for executing runtime configured controller - /// actions (e.g. minimal api defined fields and type exensions). + /// actions (e.g. minimal api defined fields and type exensions). This class can only be + /// instantiated by the library runtime. /// [GraphRoot] internal sealed class RuntimeFieldExecutionController : GraphController diff --git a/src/graphql-aspnet/Interfaces/Schema/IEnumValue.cs b/src/graphql-aspnet/Interfaces/Schema/IEnumValue.cs index 83d2854bc..aaa3a2b45 100644 --- a/src/graphql-aspnet/Interfaces/Schema/IEnumValue.cs +++ b/src/graphql-aspnet/Interfaces/Schema/IEnumValue.cs @@ -29,15 +29,20 @@ public interface IEnumValue : ISchemaItem, IDeprecatable IEnumGraphType Parent { get; } /// - /// Gets the declared numerical value of the enum. + /// Gets the declared numerical value of the enum, as its defined in source code. /// - /// The value of the neum. + /// The assigned value of the enum. object DeclaredValue { get; } /// /// Gets the declared label applied to the enum value by .NET. /// (e.g. 'Value1' for the enum value MyEnum.Value1). /// + /// + /// The casing on this value will match the enum label in source code and may be + /// different than the name value which will match + /// the rules of the target schema. + /// /// The declared label in source code. string DeclaredLabel { get; } } diff --git a/src/graphql-aspnet/Schemas/Generation/RuntimeSchemaItemDefinitions/RuntimeControllerActionDefinitionBase.cs b/src/graphql-aspnet/Schemas/Generation/RuntimeSchemaItemDefinitions/RuntimeControllerActionDefinitionBase.cs index 6384b593d..951c38de7 100644 --- a/src/graphql-aspnet/Schemas/Generation/RuntimeSchemaItemDefinitions/RuntimeControllerActionDefinitionBase.cs +++ b/src/graphql-aspnet/Schemas/Generation/RuntimeSchemaItemDefinitions/RuntimeControllerActionDefinitionBase.cs @@ -15,6 +15,7 @@ namespace GraphQL.AspNet.Schemas.Generation.RuntimeSchemaItemDefinitions using GraphQL.AspNet.Common.Extensions; using GraphQL.AspNet.Configuration; using GraphQL.AspNet.Execution; + using GraphQL.AspNet.Interfaces.Schema; using GraphQL.AspNet.Interfaces.Schema.RuntimeDefinitions; using GraphQL.AspNet.Schemas.Structural; @@ -37,7 +38,7 @@ private RuntimeControllerActionDefinitionBase() /// /// Initializes a new instance of the class. /// - /// The schema options where this field item + /// The schema options that define the future where this field item /// is being defined. /// The full item path to use for this schema item. protected RuntimeControllerActionDefinitionBase( diff --git a/src/unit-tests/graphql-aspnet-tests/Schemas/GraphFieldCloningTests.cs b/src/unit-tests/graphql-aspnet-tests/Schemas/GraphFieldCloningTests.cs index f59b79cf3..c3c0059b7 100644 --- a/src/unit-tests/graphql-aspnet-tests/Schemas/GraphFieldCloningTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Schemas/GraphFieldCloningTests.cs @@ -11,8 +11,6 @@ namespace GraphQL.AspNet.Tests.Schemas { using System.Collections.Generic; using System.Linq; - using GraphQL.AspNet.Execution; - using GraphQL.AspNet.Execution.Parsing.NodeBuilders; using GraphQL.AspNet.Interfaces.Execution; using GraphQL.AspNet.Interfaces.Schema; using GraphQL.AspNet.Schemas; diff --git a/src/unit-tests/graphql-aspnet-tests/Schemas/GraphTypeExpressionTests.cs b/src/unit-tests/graphql-aspnet-tests/Schemas/GraphTypeExpressionTests.cs index 90cbb036f..ff6ef4ee6 100644 --- a/src/unit-tests/graphql-aspnet-tests/Schemas/GraphTypeExpressionTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Schemas/GraphTypeExpressionTests.cs @@ -205,7 +205,9 @@ public void AreCompatiable(string targetExpression, string suppliedExpression, b [TestCase(typeof(IEnumerable), "[TwoPropertyObject]", null)] [TestCase(typeof(TwoPropertyObject[]), "[TwoPropertyObject]", null)] [TestCase(typeof(int[][]), "[[Int!]]", null)] + [TestCase(typeof(int?[][]), "[[Int]]", null)] [TestCase(typeof(string[]), "[String]", null)] + [TestCase(typeof(string[]), "[String!]!", new MGT[] { MGT.IsNotNull, MGT.IsList, MGT.IsNotNull })] [TestCase(typeof(KeyValuePair), "KeyValuePair_string_int_!", null)] [TestCase(typeof(KeyValuePair), "KeyValuePair_string___int_____!", null)] [TestCase(typeof(KeyValuePair), "KeyValuePair_string_int___!", null)] @@ -219,9 +221,9 @@ public void AreCompatiable(string targetExpression, string suppliedExpression, b public void GenerateTypeExpression( Type type, string expectedExpression, - MGT[] wrappers) + MGT[] overridingWrappers) { - var typeExpression = GraphTypeExpression.FromType(type, wrappers); + var typeExpression = GraphTypeExpression.FromType(type, overridingWrappers); Assert.AreEqual(expectedExpression, typeExpression.ToString()); } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy