diff --git a/README.md b/README.md
index 12835b4..a3a6b19 100644
--- a/README.md
+++ b/README.md
@@ -48,8 +48,23 @@ public Task CreateUser(CreateUserInput userInput) { ... }
### How validation errors will be handled
-By default, errors will be written out into the GraphQL execution result in the `Errors` property with one error being reported per failure on a field.
-You can change this behaviour by implementing your own `IValidationErrorsHandler`.
+Validation errors can be reported by FairyBread in one of two fashions, depending on the field in question:
+1. Globally, under the GraphQL response's `errors` array.
+2. Inline, under the mutation field's `errors` array as per [mutation conventions]().
+
+Currently, standard fields (i.e. not a mutation field), will always report errors globally.
+This may be changed in the future as best practices and upstream support comes through.
+
+Mutation fields on the other hand, when using a field is using Hot Chocolate's mutation convention
+(and FairyBread's UseMutationConvention setting is on, as is the default),
+the validation errors will be written out under the convention-based `errors` array field
+under the mutation field.
+
+Note: To assist in migration from the old behavior to the new one, you can use `[GlobalValidationErrors]` or
+`.GlobalValidationErrors()` (fluent-style) on mutation fields to opt back into global errors field-by-field.
+
+In both cases, one error will be reported per failure on a field.
+You can change this behavior by implementing your own `IValidationErrorsHandler`.
### Implicit vs explicit configuration
@@ -117,6 +132,8 @@ services.AddFairyBread(options =>
{
options.ShouldValidateArgument = (objTypeDef, fieldTypeDef, argTypeDef) => ...;
options.ThrowIfNoValidatorsFound = true/false;
+ options.UseMutationConventions = true/false;
+ options.ValidationErrorType = typeof(...);
});
```
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 940f250..27ee3c7 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -21,7 +21,7 @@
10.0.0
10.0.0
- 13.6.0-preview.31
+ 13.6.0
net7.0; net6.0; netstandard2.0
net7.0; net6.0
diff --git a/src/FairyBread/DefaultFairyBreadOptions.cs b/src/FairyBread/DefaultFairyBreadOptions.cs
index 2a52d35..c308a5f 100644
--- a/src/FairyBread/DefaultFairyBreadOptions.cs
+++ b/src/FairyBread/DefaultFairyBreadOptions.cs
@@ -15,6 +15,6 @@ public class DefaultFairyBreadOptions : IFairyBreadOptions
= true;
///
- public virtual Type ValidationExceptionType { get; set; }
- = typeof(DefaultValidationException);
+ public virtual Type ValidationErrorType { get; set; }
+ = typeof(DefaultValidationError);
}
diff --git a/src/FairyBread/DefaultValidationError.cs b/src/FairyBread/DefaultValidationError.cs
new file mode 100644
index 0000000..5880543
--- /dev/null
+++ b/src/FairyBread/DefaultValidationError.cs
@@ -0,0 +1,20 @@
+namespace FairyBread;
+
+[GraphQLName("ValidationError")]
+public class DefaultValidationError
+{
+ private DefaultValidationError(
+ string message)
+ {
+ Message = "A validation error occurred";
+ // TODO: Complete a nice rich implementation.
+ }
+
+ public static DefaultValidationError CreateErrorFrom(
+ ArgumentValidationResult argValRes)
+ {
+ return new("");
+ }
+
+ public string Message { get; }
+}
diff --git a/src/FairyBread/DefaultValidationErrorsHandler.cs b/src/FairyBread/DefaultValidationErrorsHandler.cs
index 8ed4aae..a7f63ef 100644
--- a/src/FairyBread/DefaultValidationErrorsHandler.cs
+++ b/src/FairyBread/DefaultValidationErrorsHandler.cs
@@ -6,10 +6,9 @@ public virtual void Handle(
IMiddlewareContext context,
IEnumerable invalidResults)
{
- if (context.Selection.Field.ContextData.TryGetValue(WellKnownContextData.UsesMutationConvention, out var usesMutationConventionObj) &&
- usesMutationConventionObj is bool usesMutationConvention &&
- usesMutationConvention == true)
- // TODO: And options are configured to write this into conventions now
+ if (context.Selection.Field.ContextData.TryGetValue(WellKnownContextData.UsesInlineErrors, out var usesInlineErrorsObj) &&
+ usesInlineErrorsObj is bool usesInlineErrors &&
+ usesInlineErrors == true)
{
context.Result = new MutationError(
invalidResults
diff --git a/src/FairyBread/DefaultValidationException.cs b/src/FairyBread/DefaultValidationException.cs
deleted file mode 100644
index 5266659..0000000
--- a/src/FairyBread/DefaultValidationException.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-namespace FairyBread;
-
-[GraphQLName("ValidationError")]
-public class DefaultValidationError
-{
- private DefaultValidationError(
- string message)
- {
- Message = "A validation error occurred";
- // TODO: Complete a nice rich implementation.
- }
-
- private DefaultValidationError(
- DefaultValidationException exception)
- {
- Message = "A validation error occurred";
- // TODO: Complete a nice rich implementation.
- }
-
- public static DefaultValidationError CreateErrorFrom(
- DefaultValidationException ex)
- {
- return new(ex);
- }
-
- public static DefaultValidationError CreateErrorFrom(
- ArgumentValidationResult argValRes)
- {
- return new("");
- }
-
- public string Message { get; }
-}
-
-public class DefaultValidationException : Exception
-{
- public DefaultValidationException(
- IMiddlewareContext context,
- IEnumerable invalidResults)
- {
- Context = context;
- InvalidResults = invalidResults;
- }
-
- public IMiddlewareContext Context { get; }
- public IEnumerable InvalidResults { get; }
-}
diff --git a/src/FairyBread/GlobalValidationErrorsAttribute.cs b/src/FairyBread/GlobalValidationErrorsAttribute.cs
new file mode 100644
index 0000000..e2e65dc
--- /dev/null
+++ b/src/FairyBread/GlobalValidationErrorsAttribute.cs
@@ -0,0 +1,56 @@
+
+namespace FairyBread;
+
+///
+///
+/// Instructs FairyBread, when using HotChocolate's mutation conventions
+/// and with FairyBread configured with UseMutationConventions as true,
+/// to report validation errors in the global errors array of the GraphQL
+/// response (rather than inline in this mutation fields' local errors array).
+///
+///
+/// This attribute is intended to allow a progressive transition from FairyBread's
+/// former behavior with mutation conventions (global errors) to the
+/// new behavior (local errors). Fields that you aren't ready to modernize can
+/// be annotated in the meantime.
+///
+///
+[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
+public class GlobalValidationErrorsAttribute : ObjectFieldDescriptorAttribute
+{
+ protected override void OnConfigure(
+ IDescriptorContext context,
+ IObjectFieldDescriptor descriptor,
+ MemberInfo memberInfo)
+ {
+ descriptor.GlobalValidationErrors();
+ }
+}
+
+public static class GlobalValidationErrorsObjectFieldDescriptorExtensions
+{
+ ///
+ ///
+ /// Instructs FairyBread, when using HotChocolate's mutation conventions
+ /// and with FairyBread configured with UseMutationConventions as true,
+ /// to report validation errors in the global errors array of the GraphQL
+ /// response (rather than inline in this mutation fields' local errors array).
+ ///
+ ///
+ /// This is intended to allow a progressive transition from FairyBread's
+ /// former behavior with mutation conventions (global errors) to the
+ /// new behavior (local errors). Fields that you aren't ready to modernize can
+ /// use this in the meantime.
+ ///
+ ///
+ public static IObjectFieldDescriptor GlobalValidationErrors(
+ this IObjectFieldDescriptor descriptor)
+ {
+ descriptor.Extend().OnBeforeNaming((completionContext, objFieldDef) =>
+ {
+ objFieldDef.ContextData[WellKnownContextData.UsesGlobalErrors] = true;
+ });
+
+ return descriptor;
+ }
+}
diff --git a/src/FairyBread/IFairyBreadOptions.cs b/src/FairyBread/IFairyBreadOptions.cs
index 0d333fd..2fe7ec3 100644
--- a/src/FairyBread/IFairyBreadOptions.cs
+++ b/src/FairyBread/IFairyBreadOptions.cs
@@ -18,5 +18,5 @@ public interface IFairyBreadOptions
bool UseMutationConventions { get; set; }
- Type ValidationExceptionType { get; set; }
+ Type ValidationErrorType { get; set; }
}
diff --git a/src/FairyBread/ValidationErrorConfigurer.cs b/src/FairyBread/ValidationErrorConfigurer.cs
new file mode 100644
index 0000000..184e4a9
--- /dev/null
+++ b/src/FairyBread/ValidationErrorConfigurer.cs
@@ -0,0 +1,42 @@
+using HotChocolate.Types;
+
+namespace FairyBread;
+
+internal class ValidationErrorConfigurer : MutationErrorConfiguration
+{
+ public override void OnConfigure(
+ IDescriptorContext context,
+ ObjectFieldDefinition fieldDef)
+ {
+ var options = context.Services
+ .GetRequiredService();
+ if (!options.UseMutationConventions)
+ {
+ return;
+ }
+
+ var validatorRegistry = context.Services
+ .GetRequiredService();
+
+ // TODO: Can I get this? I think I need to PR this as Michael said he'd pass this through but hasn't.
+ var objTypeDef = new ObjectTypeDefinition();
+
+ if (ValidationMiddlewareHelpers.NeedsMiddleware(options, validatorRegistry, objTypeDef, fieldDef))
+ {
+ ValidationMiddlewareHelpers.AddMiddleware(fieldDef);
+
+ if (!fieldDef.ContextData.TryGetValue(WellKnownContextData.UsesGlobalErrors, out var usesGlobalErrorsObj) ||
+ usesGlobalErrorsObj is not bool usesGlobalErrors ||
+ usesGlobalErrors == false)
+ {
+ fieldDef.AddErrorType(context, options.ValidationErrorType);
+
+ // Set a flag for the errors handler
+ fieldDef.ContextData[WellKnownContextData.UsesInlineErrors] = true;
+
+ // Cleanup
+ //fieldDef.ContextData.Remove(WellKnownContextData.UsesGlobalErrors);
+ }
+ }
+ }
+}
diff --git a/src/FairyBread/ValidationMiddlewareHelpers.cs b/src/FairyBread/ValidationMiddlewareHelpers.cs
new file mode 100644
index 0000000..f545a06
--- /dev/null
+++ b/src/FairyBread/ValidationMiddlewareHelpers.cs
@@ -0,0 +1,221 @@
+namespace FairyBread;
+
+internal static class ValidationMiddlewareHelpers
+{
+ private const string _middlewareKey = "FairyBread.ValidationMiddleware";
+ internal static Lazy _validationFieldMiddlewareDef = new(() =>
+ new FieldMiddlewareDefinition(
+ FieldClassMiddlewareFactory.Create(),
+ key: _middlewareKey));
+
+ internal static bool NeedsMiddleware(
+ IFairyBreadOptions options,
+ IValidatorRegistry validatorRegistry,
+ ObjectTypeDefinition objTypeDef,
+ ObjectFieldDefinition fieldDef)
+ {
+ // Don't add validation middleware unless:
+ // 1. we have args
+ var needsValidationMiddleware = false;
+
+ foreach (var argDef in fieldDef.Arguments)
+ {
+ // 2. the argument should be validated according to options func
+ if (!options.ShouldValidateArgument(objTypeDef, fieldDef, argDef))
+ {
+ continue;
+ }
+
+ // 3. there's validators for it
+ List validatorDescs;
+ try
+ {
+ validatorDescs = DetermineValidatorsForArg(validatorRegistry, argDef);
+ if (validatorDescs.Count < 1)
+ {
+ continue;
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new Exception(
+ $"Problem getting runtime type for argument '{argDef.Name}' " +
+ $"in field '{fieldDef.Name}' on object type '{objTypeDef.Name}'.",
+ ex);
+ }
+
+ // Cleanup context now we're done with these
+ foreach (var key in argDef.ContextData.Keys)
+ {
+ if (key.StartsWith(WellKnownContextData.Prefix))
+ {
+ argDef.ContextData.Remove(key);
+ }
+ }
+
+ validatorDescs.TrimExcess();
+ needsValidationMiddleware = true;
+ argDef.ContextData[WellKnownContextData.ValidatorDescriptors] = validatorDescs.AsReadOnly();
+ }
+
+ return needsValidationMiddleware;
+ }
+
+ internal static void AddMiddleware(ObjectFieldDefinition fieldDef)
+ {
+ // TODO: Insert if not exists
+ fieldDef.MiddlewareDefinitions.Insert(0, _validationFieldMiddlewareDef.Value);
+ }
+
+ private static List DetermineValidatorsForArg(
+ IValidatorRegistry validatorRegistry,
+ ArgumentDefinition argDef)
+ {
+ // If validation is explicitly disabled, return none so validation middleware won't be added
+ if (argDef.ContextData.ContainsKey(WellKnownContextData.DontValidate))
+ {
+ return new List(0);
+ }
+
+ var validators = new List();
+
+ // Include implicit validator/s first (if allowed)
+ if (!argDef.ContextData.ContainsKey(WellKnownContextData.DontValidateImplicitly))
+ {
+ // And if we can figure out the arg's runtime type
+ var argRuntimeType = TryGetArgRuntimeType(argDef);
+ if (argRuntimeType is not null)
+ {
+ if (validatorRegistry.Cache.TryGetValue(argRuntimeType, out var implicitValidators) &&
+ implicitValidators is not null)
+ {
+ validators.AddRange(implicitValidators);
+ }
+ }
+ }
+
+ // Include explicit validator/s (that aren't already added implicitly)
+ if (argDef.ContextData.TryGetValue(WellKnownContextData.ExplicitValidatorTypes, out var explicitValidatorTypesRaw) &&
+ explicitValidatorTypesRaw is IEnumerable explicitValidatorTypes)
+ {
+ // TODO: Potentially check and throw if there's a validator being explicitly applied for the wrong runtime type
+
+ foreach (var validatorType in explicitValidatorTypes)
+ {
+ if (validators.Any(v => v.ValidatorType == validatorType))
+ {
+ continue;
+ }
+
+ validators.Add(new ValidatorDescriptor(validatorType));
+ }
+ }
+
+ return validators;
+ }
+
+ private static Type? TryGetArgRuntimeType(
+ ArgumentDefinition argDef)
+ {
+ if (argDef.Parameter?.ParameterType is { } argRuntimeType)
+ {
+ return argRuntimeType;
+ }
+
+ if (argDef.Type is ExtendedTypeReference extTypeRef)
+ {
+ return TryGetRuntimeType(extTypeRef.Type);
+ }
+
+ return null;
+ }
+
+ private static Type? TryGetRuntimeType(IExtendedType extType)
+ {
+ // It's already a runtime type, .Type(typeof(int))
+ if (extType.Kind == ExtendedTypeKind.Runtime)
+ {
+ return extType.Source;
+ }
+
+ // Array (though not sure what produces this scenario as seems to always be list)
+ if (extType.IsArray)
+ {
+ if (extType.ElementType is null)
+ {
+ return null;
+ }
+
+ var elementRuntimeType = TryGetRuntimeType(extType.ElementType);
+ if (elementRuntimeType is null)
+ {
+ return null;
+ }
+
+ return Array.CreateInstance(elementRuntimeType, 0).GetType();
+ }
+
+ // List
+ if (extType.IsList)
+ {
+ if (extType.ElementType is null)
+ {
+ return null;
+ }
+
+ var elementRuntimeType = TryGetRuntimeType(extType.ElementType);
+ if (elementRuntimeType is null)
+ {
+ return null;
+ }
+
+ return typeof(List<>).MakeGenericType(elementRuntimeType);
+ }
+
+ // Input object
+ if (typeof(InputObjectType).IsAssignableFrom(extType))
+ {
+ var currBaseType = extType.Type.BaseType;
+ while (currBaseType is not null &&
+ (!currBaseType.IsGenericType ||
+ currBaseType.GetGenericTypeDefinition() != typeof(InputObjectType<>)))
+ {
+ currBaseType = currBaseType.BaseType;
+ }
+
+ if (currBaseType is null)
+ {
+ return null;
+ }
+
+ return currBaseType!.GenericTypeArguments[0];
+ }
+
+ // Singular scalar
+ if (typeof(ScalarType).IsAssignableFrom(extType))
+ {
+ var currBaseType = extType.Type.BaseType;
+ while (currBaseType is not null &&
+ (!currBaseType.IsGenericType ||
+ currBaseType.GetGenericTypeDefinition() != typeof(ScalarType<>)))
+ {
+ currBaseType = currBaseType.BaseType;
+ }
+
+ if (currBaseType is null)
+ {
+ return null;
+ }
+
+ var argRuntimeType = currBaseType.GenericTypeArguments[0];
+ if (argRuntimeType.IsValueType && extType.IsNullable)
+ {
+ return typeof(Nullable<>).MakeGenericType(argRuntimeType);
+ }
+
+ return argRuntimeType;
+ }
+
+ return null;
+ }
+}
diff --git a/src/FairyBread/ValidationMiddlewareInjector.cs b/src/FairyBread/ValidationMiddlewareInjector.cs
index 80f2907..24eaa92 100644
--- a/src/FairyBread/ValidationMiddlewareInjector.cs
+++ b/src/FairyBread/ValidationMiddlewareInjector.cs
@@ -1,33 +1,5 @@
namespace FairyBread;
-internal class ValidationErrorConfigurer : MutationErrorConfiguration
-{
- public override void OnConfigure(IDescriptorContext context, ObjectFieldDefinition fieldDef)
- {
- var options = context.Services
- .GetRequiredService();
- if (!options.UseMutationConventions)
- {
- return;
- }
-
- var validatorRegistry = context.Services
- .GetRequiredService();
-
- var objTypeDef = new ObjectTypeDefinition(); // TODO: Can I get this?
-
- if (Helpers.NeedsMiddleware(options, validatorRegistry, objTypeDef, fieldDef))
- {
- fieldDef.AddErrorType(context, typeof(DefaultValidationError));
-
- Helpers.AddMiddleware(fieldDef);
-
- // Set a flag that indicates to the error handler that this field uses mutation conventions
- fieldDef.ContextData[WellKnownContextData.UsesMutationConvention] = true;
- }
- }
-}
-
internal class ValidationMiddlewareInjector : TypeInterceptor
{
public override void OnBeforeRegisterDependencies(
@@ -46,229 +18,10 @@ public override void OnBeforeRegisterDependencies(
foreach (var fieldDef in objTypeDef.Fields)
{
- if (Helpers.NeedsMiddleware(options, validatorRegistry, objTypeDef, fieldDef))
- {
- Helpers.AddMiddleware(fieldDef);
- }
- }
- }
-}
-
-internal static class Helpers
-{
- private const string _middlewareKey = "FairyBread.ValidationMiddleware";
- internal static Lazy _validationFieldMiddlewareDef = new(() =>
- new FieldMiddlewareDefinition(
- FieldClassMiddlewareFactory.Create(),
- key: _middlewareKey));
-
- internal static bool NeedsMiddleware(
- IFairyBreadOptions options,
- IValidatorRegistry validatorRegistry,
- ObjectTypeDefinition objTypeDef,
- ObjectFieldDefinition fieldDef)
- {
- // Don't add validation middleware unless:
- // 1. we have args
- var needsValidationMiddleware = false;
-
- foreach (var argDef in fieldDef.Arguments)
- {
- // 2. the argument should be validated according to options func
- if (!options.ShouldValidateArgument(objTypeDef, fieldDef, argDef))
+ if (ValidationMiddlewareHelpers.NeedsMiddleware(options, validatorRegistry, objTypeDef, fieldDef))
{
- continue;
+ ValidationMiddlewareHelpers.AddMiddleware(fieldDef);
}
-
- // 3. there's validators for it
- List validatorDescs;
- try
- {
- validatorDescs = DetermineValidatorsForArg(validatorRegistry, argDef);
- if (validatorDescs.Count < 1)
- {
- continue;
- }
- }
- catch (Exception ex)
- {
- throw new Exception(
- $"Problem getting runtime type for argument '{argDef.Name}' " +
- $"in field '{fieldDef.Name}' on object type '{objTypeDef.Name}'.",
- ex);
- }
-
- // Cleanup context now we're done with these
- foreach (var key in argDef.ContextData.Keys)
- {
- if (key.StartsWith(WellKnownContextData.Prefix))
- {
- argDef.ContextData.Remove(key);
- }
- }
-
- validatorDescs.TrimExcess();
- needsValidationMiddleware = true;
- argDef.ContextData[WellKnownContextData.ValidatorDescriptors] = validatorDescs.AsReadOnly();
}
-
- return needsValidationMiddleware;
- }
-
- internal static void AddMiddleware(ObjectFieldDefinition fieldDef)
- {
- fieldDef.MiddlewareDefinitions.Insert(0, _validationFieldMiddlewareDef.Value);
- }
-
- private static List DetermineValidatorsForArg(
- IValidatorRegistry validatorRegistry,
- ArgumentDefinition argDef)
- {
- // If validation is explicitly disabled, return none so validation middleware won't be added
- if (argDef.ContextData.ContainsKey(WellKnownContextData.DontValidate))
- {
- return new List(0);
- }
-
- var validators = new List();
-
- // Include implicit validator/s first (if allowed)
- if (!argDef.ContextData.ContainsKey(WellKnownContextData.DontValidateImplicitly))
- {
- // And if we can figure out the arg's runtime type
- var argRuntimeType = TryGetArgRuntimeType(argDef);
- if (argRuntimeType is not null)
- {
- if (validatorRegistry.Cache.TryGetValue(argRuntimeType, out var implicitValidators) &&
- implicitValidators is not null)
- {
- validators.AddRange(implicitValidators);
- }
- }
- }
-
- // Include explicit validator/s (that aren't already added implicitly)
- if (argDef.ContextData.TryGetValue(WellKnownContextData.ExplicitValidatorTypes, out var explicitValidatorTypesRaw) &&
- explicitValidatorTypesRaw is IEnumerable explicitValidatorTypes)
- {
- // TODO: Potentially check and throw if there's a validator being explicitly applied for the wrong runtime type
-
- foreach (var validatorType in explicitValidatorTypes)
- {
- if (validators.Any(v => v.ValidatorType == validatorType))
- {
- continue;
- }
-
- validators.Add(new ValidatorDescriptor(validatorType));
- }
- }
-
- return validators;
- }
-
- private static Type? TryGetArgRuntimeType(
- ArgumentDefinition argDef)
- {
- if (argDef.Parameter?.ParameterType is { } argRuntimeType)
- {
- return argRuntimeType;
- }
-
- if (argDef.Type is ExtendedTypeReference extTypeRef)
- {
- return TryGetRuntimeType(extTypeRef.Type);
- }
-
- return null;
- }
-
- private static Type? TryGetRuntimeType(IExtendedType extType)
- {
- // It's already a runtime type, .Type(typeof(int))
- if (extType.Kind == ExtendedTypeKind.Runtime)
- {
- return extType.Source;
- }
-
- // Array (though not sure what produces this scenario as seems to always be list)
- if (extType.IsArray)
- {
- if (extType.ElementType is null)
- {
- return null;
- }
-
- var elementRuntimeType = TryGetRuntimeType(extType.ElementType);
- if (elementRuntimeType is null)
- {
- return null;
- }
-
- return Array.CreateInstance(elementRuntimeType, 0).GetType();
- }
-
- // List
- if (extType.IsList)
- {
- if (extType.ElementType is null)
- {
- return null;
- }
-
- var elementRuntimeType = TryGetRuntimeType(extType.ElementType);
- if (elementRuntimeType is null)
- {
- return null;
- }
-
- return typeof(List<>).MakeGenericType(elementRuntimeType);
- }
-
- // Input object
- if (typeof(InputObjectType).IsAssignableFrom(extType))
- {
- var currBaseType = extType.Type.BaseType;
- while (currBaseType is not null &&
- (!currBaseType.IsGenericType ||
- currBaseType.GetGenericTypeDefinition() != typeof(InputObjectType<>)))
- {
- currBaseType = currBaseType.BaseType;
- }
-
- if (currBaseType is null)
- {
- return null;
- }
-
- return currBaseType!.GenericTypeArguments[0];
- }
-
- // Singular scalar
- if (typeof(ScalarType).IsAssignableFrom(extType))
- {
- var currBaseType = extType.Type.BaseType;
- while (currBaseType is not null &&
- (!currBaseType.IsGenericType ||
- currBaseType.GetGenericTypeDefinition() != typeof(ScalarType<>)))
- {
- currBaseType = currBaseType.BaseType;
- }
-
- if (currBaseType is null)
- {
- return null;
- }
-
- var argRuntimeType = currBaseType.GenericTypeArguments[0];
- if (argRuntimeType.IsValueType && extType.IsNullable)
- {
- return typeof(Nullable<>).MakeGenericType(argRuntimeType);
- }
-
- return argRuntimeType;
- }
-
- return null;
}
}
diff --git a/src/FairyBread/WellKnownContextData.cs b/src/FairyBread/WellKnownContextData.cs
index 959190a..95f8c16 100644
--- a/src/FairyBread/WellKnownContextData.cs
+++ b/src/FairyBread/WellKnownContextData.cs
@@ -16,6 +16,9 @@ internal static class WellKnownContextData
public const string ValidatorDescriptors =
Prefix + "Validators";
- public const string UsesMutationConvention =
- Prefix + "UsesMutationConvention";
+ public const string UsesInlineErrors =
+ Prefix + "UsesInlineErrors";
+
+ public const string UsesGlobalErrors =
+ Prefix + "UsesGlobalErrors";
}