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-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.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/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/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/GraphNameFormatStrategy.cs b/src/graphql-aspnet/Configuration/Formatting/GraphNameFormatStrategy.cs deleted file mode 100644 index 28690dcaf..000000000 --- a/src/graphql-aspnet/Configuration/Formatting/GraphNameFormatStrategy.cs +++ /dev/null @@ -1,24 +0,0 @@ -// ************************************************************* -// project: graphql-aspnet -// -- -// repo: https://github.com/graphql-aspnet -// docs: https://graphql-aspnet.github.io -// -- -// License: MIT -// ************************************************************* - -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 - { - ProperCase, - CamelCase, - UpperCase, - LowerCase, - NoChanges, - } -} \ No newline at end of file diff --git a/src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategy.cs b/src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategy.cs deleted file mode 100644 index 148b2247c..000000000 --- a/src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategy.cs +++ /dev/null @@ -1,399 +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; - using System.IO; - using GraphQL.AspNet.Common.Extensions; - 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 - /// to schema items as they are being constructed. - /// - public class GraphSchemaFormatStrategy - { - /// - /// Initializes a new instance of the class. - /// - /// A single format strategy to use for all naming formats. - public GraphSchemaFormatStrategy(GraphNameFormatStrategy singleStrategy) - : this( - NullabilityFormatStrategy.Default, - singleStrategy, - singleStrategy, - singleStrategy) - { - } - - /// - /// 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) - { - this.NullabilityStrategy = nullabilityStrategy; - this.GraphTypeNameStrategy = typeNameStrategy; - this.FieldNameStrategy = fieldNameStrategy; - 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) - 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 a field or argument name according to the strategy declared on this formatter. - /// - /// The field or argument name to format. - /// System.String. - public virtual string FormatFieldName(string name) - { - return this.FormatName(name, FieldNameStrategy); - } - - /// - /// Formats the enum value name according to the strategy declared on this formatter. - /// - /// The enum value name to format. - /// System.String. - public virtual string FormatEnumValueName(string name) - { - return this.FormatName(name, EnumValueStrategy); - } - - /// - /// Formats the graph type name according to the strategy declared on this formatter. - /// - /// The type name to format. - /// System.String. - public 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); - } - - /// - /// 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) - { - if (graphType is IScalarGraphType scalarType) - { - if (!GlobalTypes.CanBeRenamed(scalarType.Name)) - return graphType; - } - - 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) - { - 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.ApplyTypeExpressionNullabilityStrategy(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 . - /// - /// The configuration to read formatting settings - /// from. - /// The argument to update. - /// IGraphField. - protected virtual IGraphArgument FormatArgument(ISchemaConfiguration configuration, IGraphArgument argument) - { - if (!argument.TypeExpression.IsFixed) - argument = this.ApplyTypeExpressionNullabilityStrategy(argument); - - var formattedName = this.FormatFieldName(argument.Name); - var typeExpression = argument.TypeExpression; - typeExpression = typeExpression.Clone(this.FormatGraphTypeName(typeExpression.TypeName)); - - return argument.Clone(argumentName: formattedName, typeExpression: typeExpression); - } - - /// - /// 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 ApplyTypeExpressionNullabilityStrategy(IGraphField graphField) - { - GraphTypeExpressionNullabilityStrategies strat = GraphTypeExpressionNullabilityStrategies.None; - var shouldBeNonNullType = this.NullabilityStrategy - .HasFlag(NullabilityFormatStrategy.NonNullTemplates) - && graphField.IsVirtual; - - shouldBeNonNullType = shouldBeNonNullType || - (this.NullabilityStrategy - .HasFlag(NullabilityFormatStrategy.NonNullStrings) - && graphField.ObjectType == typeof(string)); - - shouldBeNonNullType = shouldBeNonNullType || - (this.NullabilityStrategy - .HasFlag(NullabilityFormatStrategy.NonNullReferenceTypes) - && !graphField.IsVirtual - && graphField.ObjectType != typeof(string) - && !graphField.ObjectType.IsValueType); - - if (shouldBeNonNullType) - strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullType; - - if (this.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullLists)) - strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullLists; - - var newTypeExpression = graphField.TypeExpression.Clone(strat); - return graphField.Clone(typeExpression: newTypeExpression); - } - - /// - /// For the given argument applies an appropriate nullability strategy - /// according to the rules of this instance and returns a new instance - /// of the argument. - /// - /// The argument to update. - /// IGraphField. - protected virtual IGraphArgument ApplyTypeExpressionNullabilityStrategy(IGraphArgument argument) - { - GraphTypeExpressionNullabilityStrategies strat = GraphTypeExpressionNullabilityStrategies.None; - - var shouldBeNonNullType = this.NullabilityStrategy - .HasFlag(NullabilityFormatStrategy.NonNullStrings) - && argument.ObjectType == typeof(string); - - shouldBeNonNullType = shouldBeNonNullType || - (this.NullabilityStrategy - .HasFlag(NullabilityFormatStrategy.NonNullReferenceTypes) - && argument.ObjectType != typeof(string) - && !argument.ObjectType.IsValueType); - - if (shouldBeNonNullType) - strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullType; - - if (this.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullLists)) - strat = strat | GraphTypeExpressionNullabilityStrategies.NonNullLists; - - var newTypeExpression = argument.TypeExpression.Clone(strat); - return argument.Clone(typeExpression: newTypeExpression); - } - - /// - /// 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, GraphNameFormatStrategy strategy) - { - if (name == null) - return null; - - switch (strategy) - { - case GraphNameFormatStrategy.ProperCase: - return name.FirstCharacterToUpperInvariant(); - - case GraphNameFormatStrategy.CamelCase: - return name.FirstCharacterToLowerInvariant(); - - case GraphNameFormatStrategy.UpperCase: - return name.ToUpperInvariant(); - - case GraphNameFormatStrategy.LowerCase: - return name.ToLowerInvariant(); - - // ReSharper disable once RedundantCaseLabel - case GraphNameFormatStrategy.NoChanges: - default: - return name; - } - } - - /// - /// Gets or sets the bitwise flags that make up the nullability strategy - /// to apply to field and argument type expressions. - /// - /// The nullability strategy. - public NullabilityFormatStrategy 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; } - - /// - /// Gets or sets the name format strategy to use for field names. - /// - /// The field strategy. - public GraphNameFormatStrategy 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 GraphNameFormatStrategy 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/GraphSchemaFormatStrategyBuilder.cs deleted file mode 100644 index 32c54493e..000000000 --- a/src/graphql-aspnet/Configuration/Formatting/GraphSchemaFormatStrategyBuilder.cs +++ /dev/null @@ -1,144 +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 builder class to formulate a - /// with various options. - /// - public class GraphSchemaFormatStrategyBuilder - { - /// - /// Starts a new strategy builder instance. - /// - /// SchemaFormatStrategyBuilder. - public static GraphSchemaFormatStrategyBuilder Create() - { - return new GraphSchemaFormatStrategyBuilder(); - } - - private readonly GraphSchemaFormatStrategy _format; - - /// - /// Initializes a new instance of the class. - /// - public GraphSchemaFormatStrategyBuilder() - { - _format = new GraphSchemaFormatStrategy(); - } - - /// - /// 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. - /// - /// SchemaFormatStrategyBuilder. - public GraphSchemaFormatStrategyBuilder WithRequiredStrings() - { - if (!_format.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullStrings)) - _format.NullabilityStrategy = _format.NullabilityStrategy | NullabilityFormatStrategy.NonNullStrings; - - return this; - } - - /// - /// 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. - /// - /// SchemaFormatStrategyBuilder. - public GraphSchemaFormatStrategyBuilder WithRequiredLists() - { - if (!_format.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullLists)) - _format.NullabilityStrategy = _format.NullabilityStrategy | NullabilityFormatStrategy.NonNullLists; - - return this; - } - - /// - /// 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. - /// - /// SchemaFormatStrategyBuilder. - public GraphSchemaFormatStrategyBuilder WithRequiredObjects() - { - if (!_format.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullReferenceTypes)) - _format.NullabilityStrategy = _format.NullabilityStrategy | NullabilityFormatStrategy.NonNullReferenceTypes; - - return this; - } - - /// - /// 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. - /// - /// SchemaFormatStrategyBuilder. - public GraphSchemaFormatStrategyBuilder ClearNullabilityRules() - { - _format.NullabilityStrategy = NullabilityFormatStrategy.None; - return this; - } - - /// - /// Sets the formating of graph type names (object, interfaces etc.) to the supplied built in strategy. - /// - /// - /// DEFAULT: ProperCase - /// - /// The strategy to employ for graph type names. - /// GraphSchemaFormatStrategyBuilder. - public GraphSchemaFormatStrategyBuilder WithGraphTypeNameFormat(GraphNameFormatStrategy strategy) - { - _format.GraphTypeNameStrategy = strategy; - return this; - } - - /// - /// Sets the formating of field names to the supplied built in strategy. - /// - /// - /// DEFAULT: camelCase - /// - /// The strategy to employ for field names. - /// GraphSchemaFormatStrategyBuilder. - public GraphSchemaFormatStrategyBuilder WithFieldNameFormat(GraphNameFormatStrategy strategy) - { - _format.FieldNameStrategy = strategy; - return this; - } - - /// - /// Sets the formating of enum values to the supplied built in strategy. - /// - /// - /// Default: UPPER CASE - /// - /// The strategy to employ for graph type names. - /// GraphSchemaFormatStrategyBuilder. - public GraphSchemaFormatStrategyBuilder WithEnumValueFormat(GraphNameFormatStrategy strategy) - { - _format.EnumValueStrategy = strategy; - return this; - } - - /// - /// Returns the format strategy instance being built. - /// - /// GraphSchemaFormatStrategy. - public GraphSchemaFormatStrategy Build() - { - return _format; - } - } -} \ No newline at end of file diff --git a/src/graphql-aspnet/Configuration/Formatting/GraphTypeNullabilityStrategy.cs b/src/graphql-aspnet/Configuration/Formatting/GraphTypeNullabilityStrategy.cs deleted file mode 100644 index b42946361..000000000 --- a/src/graphql-aspnet/Configuration/Formatting/GraphTypeNullabilityStrategy.cs +++ /dev/null @@ -1,59 +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; - using System.Data; - - /// - /// A bitwise set of format strategies that can be applied out of the box. - /// - [Flags] - public enum NullabilityFormatStrategy - { - /// - /// 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. - /// - NonNullTemplates = 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 are treated like value types and cannot be null by default. - /// - NonNullStrings = 4, - - /// - /// The schema will treat all class reference types, in any type expression, as being non-nullable by - /// default. - /// - NonNullReferenceTypes = 8, - - /// - /// 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 = NonNullTemplates, - } -} \ No newline at end of file diff --git a/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.cs new file mode 100644 index 000000000..ab448a17b --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategy.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 +{ + using System.Collections.Generic; + using System.Linq; + using GraphQL.AspNet.Interfaces.Configuration; + using GraphQL.AspNet.Interfaces.Schema; + + /// + /// A strategy, employed by a schema, to make programatic alterations + /// to schema items as they are being constructed. + /// + public class SchemaFormatStrategy : ISchemaFormatStrategy + { + private List _formatRules; + + /// + /// Initializes a new instance of the class. + /// + /// The format rules to apply to schema items passing through + /// this instance. + public SchemaFormatStrategy(params ISchemaItemFormatRule[] formatRules) + { + _formatRules = formatRules.ToList() ?? []; + } + + /// + public virtual T ApplySchemaItemRules(ISchemaConfiguration configuration, T schemaItem) + where T : ISchemaItem + { + foreach (var rule in _formatRules) + schemaItem = rule.Execute(schemaItem); + + return schemaItem; + } + } +} \ No newline at end of file diff --git a/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategyBuilder.cs b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategyBuilder.cs new file mode 100644 index 000000000..f65698e30 --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/SchemaFormatStrategyBuilder.cs @@ -0,0 +1,402 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +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 + /// by applying a varied set of rules according to the devleoper's configuration requirements. + /// + public class SchemaFormatStrategyBuilder + { + /// + /// Creates a format strategy builder and applies the given format option for all name format rules. + /// + /// 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 CreateInternal( + false, + formatOption, + formatOption, + formatOption, + formatOption, + formatOption); + } + + /// + /// Creates a format strategy builder with the provided text format overrides + /// will be applied as defined. + /// + /// 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) + { + return CreateInternal( + applyDefaultRules, + typeNameFormat, + fieldNameFormat, + enumValueNameFormat, + argumentNameFormat, + directiveNameFormat); + } + + private static SchemaFormatStrategyBuilder CreateInternal( + bool applyDefaultRules, + TextFormatOptions? typeNameFormat, + TextFormatOptions? fieldNameFormat, + TextFormatOptions? enumValueNameFormat, + TextFormatOptions? argumentNameFormat, + TextFormatOptions? directiveNameFormat) + { + var builder = new SchemaFormatStrategyBuilder(); + + if (!applyDefaultRules) + builder = builder.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; + } + + 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; + + /// + /// Initializes a new instance of the class. + /// + protected SchemaFormatStrategyBuilder() + { + _customRules = new List(); + + // set all aspects of virtual fields as "non null" by default + _includeIntermediateTypeNonNullRules = true; + } + + /// + /// 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; + } + + /// + /// 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 . + /// + /// Example: [String] => [String!] + /// The predicate function that a field must match for the rule to be invoked. + /// SchemaFormatStrategyBuilder. + public virtual SchemaFormatStrategyBuilder DeclareInputFieldValuesAsNonNull(Func predicate) + { + Validation.ThrowIfNull(predicate, nameof(predicate)); + + return this.DeclareCustomRule(new ApplyInputFieldNonNullItemTypeExpressionFormatRule(predicate)); + } + + /// + /// 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 . + /// + /// Example: [String] => [String]! + /// The predicate function that a field must match for the rule to be invoked. + /// SchemaFormatStrategyBuilder. + public virtual SchemaFormatStrategyBuilder DeclareInputFieldListsAsNonNull(Func predicate) + { + return this.DeclareCustomRule(new ApplyInputFieldNonNullListTypeExpressionFormatRule(predicate)); + } + + /// + /// 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 virtual SchemaFormatStrategyBuilder DeclareFieldValuesAsNonNull(Func predicate) + { + Validation.ThrowIfNull(predicate, nameof(predicate)); + return this.DeclareCustomRule(new ApplyFieldNonNullItemTypeExpressionFormatRule(predicate)); + } + + /// + /// 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 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 virtual SchemaFormatStrategyBuilder DeclareArgumentValuesAsNonNull(Func predicate) + { + Validation.ThrowIfNull(predicate, nameof(predicate)); + return this.DeclareCustomRule(new ApplyArgumentNonNullItemTypeExpressionFormatRule(predicate)); + } + + /// + /// 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)); + } + + /// + /// 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 virtual SchemaFormatStrategyBuilder DeclareCustomRule(ISchemaItemFormatRule rule) + { + Validation.ThrowIfNull(rule, nameof(rule)); + _customRules.Add(rule); + return this; + } + + /// + /// Sets the formating of graph type names (object, interface etc.) to the supplied built in strategy. + /// + /// + /// DEFAULT: ProperCase + /// + /// The strategy to use for graph type names. + /// SchemaFormatStrategyBuilder. + public virtual SchemaFormatStrategyBuilder WithGraphTypeNameFormat(TextFormatOptions format) + { + _typeNameFormat = format; + return this; + } + + /// + /// Sets the formating of field names on INPUT_OBJECT, OBJECT and INTERFACE types + /// to the supplied built in strategy. + /// + /// + /// DEFAULT: camelCase + /// + /// The format to use for field names. + /// SchemaFormatStrategyBuilder. + public virtual SchemaFormatStrategyBuilder WithFieldNameFormat(TextFormatOptions format) + { + _fieldNameFormat = format; + return this; + } + + /// + /// Sets the formating of enum values to the supplied built in strategy. + /// + /// + /// Default: UPPER CASE + /// + /// 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 virtual SchemaFormatStrategyBuilder Clear() + { + _customRules.Clear(); + + _typeNameFormat = null; + _fieldNameFormat = null; + _enumValueFormat = null; + _argumentNameFormat = null; + _directiveNameFormat = null; + + _includeIntermediateTypeNonNullRules = false; + + return this; + } + + /// + /// Creates the format strategy from all the rules and options set on this builder. + /// + /// 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 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/Formatting/SchemaItemNameFormatOptions.cs b/src/graphql-aspnet/Configuration/Formatting/SchemaItemNameFormatOptions.cs new file mode 100644 index 000000000..ac00b307e --- /dev/null +++ b/src/graphql-aspnet/Configuration/Formatting/SchemaItemNameFormatOptions.cs @@ -0,0 +1,43 @@ +// ************************************************************* +// project: graphql-aspnet +// -- +// repo: https://github.com/graphql-aspnet +// docs: https://graphql-aspnet.github.io +// -- +// License: MIT +// ************************************************************* + +namespace GraphQL.AspNet.Configuration.Formatting +{ + /// + /// A set of internally supported name formatting options for various names and values created + /// for a schema. + /// + 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/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/SchemaDeclarationConfiguration.cs b/src/graphql-aspnet/Configuration/SchemaDeclarationConfiguration.cs index 41840758a..3d0df73da 100644 --- a/src/graphql-aspnet/Configuration/SchemaDeclarationConfiguration.cs +++ b/src/graphql-aspnet/Configuration/SchemaDeclarationConfiguration.cs @@ -32,6 +32,7 @@ public SchemaDeclarationConfiguration() this.AllowedOperations.Add(GraphOperationType.Query); this.AllowedOperations.Add(GraphOperationType.Mutation); this.ArgumentBindingRule = SchemaArgumentBindingRules.ParametersPreferQueryResolution; + this.SchemaFormatStrategy = SchemaFormatStrategyBuilder.Create().Build(); } /// @@ -46,7 +47,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 +66,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; } /// public HashSet AllowedOperations { get; } 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/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/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/Engine/DefaultGraphQLSchemaFactory_Controllers.cs b/src/graphql-aspnet/Engine/DefaultGraphQLSchemaFactory_Controllers.cs index 29aa72a0e..1be393b36 100644 --- a/src/graphql-aspnet/Engine/DefaultGraphQLSchemaFactory_Controllers.cs +++ b/src/graphql-aspnet/Engine/DefaultGraphQLSchemaFactory_Controllers.cs @@ -10,12 +10,12 @@ namespace GraphQL.AspNet.Engine { using System; - using System.Collections.Generic; using GraphQL.AspNet.Common; 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; @@ -102,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 - .FormatFieldName(segment.Name); - 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) @@ -124,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) @@ -136,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, @@ -182,61 +202,11 @@ protected virtual IObjectGraphType CreateVirtualFieldOnParent( .Configuration .DeclarationOptions .SchemaFormatStrategy? - .ApplyFormatting( + .ApplySchemaItemRules( 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? - .ApplyFormatting( - 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.FormatGraphTypeName(pathSegmentName)); - break; - } - } - - segments.Reverse(); - return string.Join("_", segments); + return (childField, returnedGraphType); } /// @@ -258,7 +228,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/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 18cfb8eb4..002336e50 100644 --- a/src/graphql-aspnet/Execution/Response/ResponseWriterBase.cs +++ b/src/graphql-aspnet/Execution/Response/ResponseWriterBase.cs @@ -15,10 +15,10 @@ namespace GraphQL.AspNet.Execution.Response using GraphQL.AspNet.Common; using GraphQL.AspNet.Common.Extensions; using GraphQL.AspNet.Execution.Source; - using GraphQL.AspNet.Configuration.Formatting; + 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 @@ -97,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) { @@ -248,7 +248,7 @@ protected virtual void WriteLeafValue(Utf8JsonWriter writer, object value, bool } if (value.GetType().IsEnum) - value = this.Formatter.FormatEnumValueName(value.ToString()); + value = value.ToString(); switch (value) { @@ -312,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; @@ -320,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) @@ -374,7 +372,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..fb85dfc53 --- /dev/null +++ b/src/graphql-aspnet/Interfaces/Configuration/ISchemaFormatStrategy.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.Interfaces.Configuration +{ + 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; + } +} diff --git a/src/graphql-aspnet/Interfaces/Configuration/ISchemaItemFormatRule.cs b/src/graphql-aspnet/Interfaces/Configuration/ISchemaItemFormatRule.cs new file mode 100644 index 000000000..e1caaba8c --- /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 TSchemaItemType Execute(TSchemaItemType schemaItem) + where TSchemaItemType : ISchemaItem; + } +} 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/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/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/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/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/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/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/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/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/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/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/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/VirtualGraphField.cs b/src/graphql-aspnet/Schemas/TypeSystem/VirtualGraphField.cs index 00e33e975..97b28820f 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}"; @@ -75,6 +73,8 @@ public VirtualGraphField( this.Publish = true; this.IsDeprecated = false; this.DeprecationReason = null; + + this.TypeExpression = this.TypeExpression.ToFixed(); } /// @@ -99,7 +99,7 @@ public IGraphField Clone( var clonedItem = new VirtualGraphField( fieldName, itemPath, - typeExpression.TypeName); + typeExpression); clonedItem.Description = this.Description; clonedItem.TypeExpression = typeExpression.Clone(); @@ -130,13 +130,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/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/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/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/GraphQLSchemaHelperMethods.cs b/src/unit-tests/graphql-aspnet-testframework/GraphQLSchemaHelperMethods.cs index 855282697..b13de74ce 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,9 @@ public static void SetNoAlterationConfiguration(this ISchema schema) { var declarationOptions = new SchemaDeclarationConfiguration(); declarationOptions.Merge(schema.Configuration.DeclarationOptions); - declarationOptions.SchemaFormatStrategy = new GraphSchemaFormatStrategy(GraphNameFormatStrategy.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 357a1544f..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 = new GraphSchemaFormatStrategy(GraphNameFormatStrategy.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-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/ConfigurationFieldNamingTests.cs b/src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationFieldNamingTests.cs index 0b89a633c..15f537f2c 100644 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationFieldNamingTests.cs +++ b/src/unit-tests/graphql-aspnet-tests/Configuration/ConfigurationFieldNamingTests.cs @@ -52,13 +52,15 @@ public async Task ProductionDefaults_UpperCaseFieldNames() builder.AddGraphQL(o => { - o.DeclarationOptions.SchemaFormatStrategy = new GraphSchemaFormatStrategy(fieldNameStrategy: GraphNameFormatStrategy.UpperCase); + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategyBuilder + .Create(fieldNameFormat: TextFormatOptions.UpperCase, applyDefaultRules: false) + .Build(); }); 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 +86,9 @@ public async Task ProductionDefaults_LowerCaseEnumValues() builder.AddGraphQL(o => { - o.DeclarationOptions.SchemaFormatStrategy = new GraphSchemaFormatStrategy(enumValueStrategy: GraphNameFormatStrategy.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 = new GraphSchemaFormatStrategy(typeNameStrategy: GraphNameFormatStrategy.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/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/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/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/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 deleted file mode 100644 index 2d33798bb..000000000 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFomatStrategyTests.cs +++ /dev/null @@ -1,322 +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 System.Threading.Tasks; - using GraphQL.AspNet.Configuration.Formatting; - using GraphQL.AspNet.Interfaces.Schema; - using GraphQL.AspNet.Tests.Common.CommonHelpers; - using GraphQL.AspNet.Tests.Configuration.FormatStrategyTestData; - using GraphQL.AspNet.Tests.Framework; - using NUnit.Framework; - using NFS = GraphQL.AspNet.Configuration.Formatting.NullabilityFormatStrategy; - - [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!")] - - // 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")] - - // 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!")] - - // string type, not fixed, object graph type - [TestCase(NFS.None, typeof(Widget), "stringProp", "String")] - [TestCase(NFS.NonNullStrings, 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")] - - // string, fixed, object graph type - [TestCase(NFS.None, typeof(Widget), "fixedStringProp", "String")] - [TestCase(NFS.NonNullStrings, 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")] - - // 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!")] - - // 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")] - - // 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!")] - - // 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")] - - // 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!")] - - // string type, not fixed, interface graph type - [TestCase(NFS.None, typeof(IWidgetInterface), "stringProp", "String")] - [TestCase(NFS.NonNullStrings, 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")] - - // string, fixed, interface graph type - [TestCase(NFS.None, typeof(IWidgetInterface), "fixedStringProp", "String")] - [TestCase(NFS.NonNullStrings, 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")] - - // 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!")] - - // 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")] - public void Integration_ConcreteObjectTypes_FieldTypeExpressionTests( - NFS strategy, - Type concreteType, - string fieldName, - string expectedTypeExpression) - { - var formatter = new GraphSchemaFormatStrategy(); - 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(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.NonNullLists, "Query_Widgets", "path1", "Query_Widgets_Path1")] - [TestCase(NFS.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.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( - NFS strategy, - string graphTypeName, - string fieldName, - string expectedTypeExpression) - { - var formatter = new GraphSchemaFormatStrategy(); - 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(NFS.None, "intArgument", "Int!")] - [TestCase(NFS.NonNullTemplates, "intArgument", "Int!")] - [TestCase(NFS.NonNullStrings, "intArgument", "Int!")] - [TestCase(NFS.NonNullReferenceTypes, "intArgument", "Int!")] - - [TestCase(NFS.None, "intArgumentFixed", "Int!")] - [TestCase(NFS.NonNullTemplates, "intArgumentFixed", "Int!")] - [TestCase(NFS.NonNullStrings, "intArgumentFixed", "Int!")] - [TestCase(NFS.NonNullReferenceTypes, "intArgumentFixed", "Int!")] - - [TestCase(NFS.None, "stringArgument", "String")] - [TestCase(NFS.NonNullTemplates, "stringArgument", "String")] - [TestCase(NFS.NonNullStrings, "stringArgument", "String!")] - [TestCase(NFS.NonNullReferenceTypes, "stringArgument", "String")] - - [TestCase(NFS.None, "stringArgumentFixed", "String")] - [TestCase(NFS.NonNullTemplates, "stringArgumentFixed", "String")] - [TestCase(NFS.NonNullStrings, "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")] - public void Integration_ArgumentTypeExpressionNullabilityTests( - NFS strategy, - string fieldName, - string expectedTypeExpression) - { - var formatter = new GraphSchemaFormatStrategy(); - 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(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]]]")] - public void Integration_Objects_FieldTypeExpressionListNullabilityTests( - NFS strategy, - string fieldName, - string expectedTypeExpression) - { - var formatter = new GraphSchemaFormatStrategy(); - formatter.NullabilityStrategy = strategy; - - var server = new TestServerBuilder() - .AddGraphQL(o => - { - o.AddType(); - 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()); - } - - [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.NonNullLists, "stringArgument", "[String]!")] - [TestCase(NFS.NonNullLists | NFS.NonNullStrings, "stringArgument", "[String!]!")] - - [TestCase(NFS.None, "stringArgumentFixed", "[String]")] - [TestCase(NFS.NonNullStrings, "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]")] - public void Integration_Controllers_FieldTypeExpressionListNullabilityTests( - NFS strategy, - string fieldName, - string expectedTypeExpression) - { - var formatter = new GraphSchemaFormatStrategy(); - 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()); - } - } -} \ 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 05f92c71c..000000000 --- a/src/unit-tests/graphql-aspnet-tests/Configuration/GraphSchemaFormatStrategyBuilderTests.cs +++ /dev/null @@ -1,117 +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 = GraphSchemaFormatStrategyBuilder - .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); - } - - [Test] - public void WithGraphTypeNameFormat_UpdatesOnlyTypeNameStrategy() - { - var strat = GraphSchemaFormatStrategyBuilder - .Create() - .WithGraphTypeNameFormat(GraphNameFormatStrategy.NoChanges) - .Build(); - - Assert.AreEqual(GraphNameFormatStrategy.NoChanges, strat.GraphTypeNameStrategy); - Assert.AreEqual(GraphNameFormatStrategy.CamelCase, strat.FieldNameStrategy); - Assert.AreEqual(GraphNameFormatStrategy.UpperCase, strat.EnumValueStrategy); - } - - [Test] - public void WithFieldNameFormat_UpdatesOnlyFieldNameStrategy() - { - var strat = GraphSchemaFormatStrategyBuilder - .Create() - .WithFieldNameFormat(GraphNameFormatStrategy.NoChanges) - .Build(); - - Assert.AreEqual(GraphNameFormatStrategy.ProperCase, strat.GraphTypeNameStrategy); - Assert.AreEqual(GraphNameFormatStrategy.NoChanges, strat.FieldNameStrategy); - Assert.AreEqual(GraphNameFormatStrategy.UpperCase, strat.EnumValueStrategy); - } - - [Test] - public void WithEnumValueFormat_UpdatesOnlyEnumValueStrategy() - { - var strat = GraphSchemaFormatStrategyBuilder - .Create() - .WithEnumValueFormat(GraphNameFormatStrategy.NoChanges) - .Build(); - - Assert.AreEqual(GraphNameFormatStrategy.ProperCase, strat.GraphTypeNameStrategy); - Assert.AreEqual(GraphNameFormatStrategy.CamelCase, strat.FieldNameStrategy); - Assert.AreEqual(GraphNameFormatStrategy.NoChanges, strat.EnumValueStrategy); - } - - [Test] - public void ClearNullabilityRules_UpdatesNullabilityStrategy() - { - var strat = GraphSchemaFormatStrategyBuilder - .Create() - .ClearNullabilityRules() - .Build(); - - Assert.AreEqual(NullabilityFormatStrategy.None, strat.NullabilityStrategy); - } - - [Test] - public void WithRequiredObjects_UpdatesNullabilityStrategy() - { - var strat = GraphSchemaFormatStrategyBuilder - .Create() - .WithRequiredObjects() - .Build(); - - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullTemplates)); - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullReferenceTypes)); - } - - [Test] - public void WithRequiredStrings_UpdatesNullabilityStrategy() - { - var strat = GraphSchemaFormatStrategyBuilder - .Create() - .WithRequiredStrings() - .Build(); - - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullTemplates)); - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullStrings)); - } - - [Test] - public void WithRequiredLists_UpdatesNullabilityStrategy() - { - var strat = GraphSchemaFormatStrategyBuilder - .Create() - .WithRequiredLists() - .Build(); - - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullTemplates)); - Assert.IsTrue(strat.NullabilityStrategy.HasFlag(NullabilityFormatStrategy.NonNullLists)); - } - } -} \ 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..d7350dc57 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,11 @@ 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 = SchemaFormatStrategyBuilder + .Create(applyDefaultRules: false) + .Build(); }); var server = serverBuilder.Build(); @@ -138,7 +144,7 @@ public async Task StandardExcution_OutputTest() ""path"": [""simple""], ""fieldName"": ""simple"", ""parentType"" : ""Query"", - ""returnType"": ""Query_Simple!"", + ""returnType"": ""Query_Simple"", ""startOffset"": [simpleStartOffset], ""duration"": [simpleDuration] }, @@ -178,7 +184,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 +208,9 @@ public async Task Tracing_ThroughBatchTypeExtension_WithSingleObjectPerSourceRes serverBuilder.AddGraphQL(o => { o.ResponseOptions.ExposeMetrics = true; + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategyBuilder + .Create(applyDefaultRules: false) + .Build(); }); var batchService = Substitute.For(); @@ -258,7 +267,7 @@ public async Task Tracing_ThroughBatchTypeExtension_WithSingleObjectPerSourceRes ""path"": [ ""batch""], ""parentType"": ""Query"", ""fieldName"": ""batch"", - ""returnType"": ""Query_Batch!"", + ""returnType"": ""Query_Batch"", ""startOffset"": """", ""duration"": """" }, @@ -346,6 +355,9 @@ public async Task Tracing_ThroughBatchTypeExtension_WithMultiObjectPerSourceResu serverBuilder.AddGraphQL(o => { o.ResponseOptions.ExposeMetrics = true; + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategyBuilder + .Create(applyDefaultRules: false) + .Build(); }); var batchService = Substitute.For(); @@ -429,7 +441,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 5b70e5994..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 = - new GraphSchemaFormatStrategy(enumValueStrategy: GraphNameFormatStrategy.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 = new GraphSchemaFormatStrategy(GraphNameFormatStrategy.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 7ddec4397..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 @@ -42,36 +42,37 @@ 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), 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), 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), 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 GraphSchemaFormatStrategy(strategy); + o.DeclarationOptions.SchemaFormatStrategy = SchemaFormatStrategyBuilder + .Create(nameFormat, applyDefaultRules: false) + .Build(); }) .Build(); 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