/// <summary> /// Instantiates the graph field according to the data provided. /// </summary> /// <param name="formatter">The formatter.</param> /// <param name="template">The template.</param> /// <param name="securityGroups">The security groups.</param> /// <returns>MethodGraphField.</returns> protected virtual MethodGraphField InstantiateField( GraphNameFormatter formatter, IGraphTypeFieldTemplate template, List <FieldSecurityGroup> securityGroups) { switch (template.FieldSource) { case GraphFieldTemplateSource.Method: case GraphFieldTemplateSource.Action: return(new MethodGraphField( formatter.FormatFieldName(template.Name), template.TypeExpression.CloneTo(formatter.FormatGraphTypeName(template.TypeExpression.TypeName)), template.Route, template.Mode, template.CreateResolver(), securityGroups)); case GraphFieldTemplateSource.Property: return(new PropertyGraphField( formatter.FormatFieldName(template.Name), template.TypeExpression.CloneTo(formatter.FormatGraphTypeName(template.TypeExpression.TypeName)), template.Route, template.DeclaredReturnType, template.DeclaredName, template.Mode, template.CreateResolver(), securityGroups)); default: throw new ArgumentOutOfRangeException($"Template field source of {template.FieldSource.ToString()} is not supported by {this.GetType().FriendlyName()}."); } }
private IGraphField MakeGraphField(IGraphTypeFieldTemplate fieldTemplate) { var testServer = new TestServerBuilder().Build(); var maker = new GraphFieldMaker(testServer.Schema); return(maker.CreateField(fieldTemplate).Field); }
/// <summary> /// Adds the type extension to the schema for the configured concrete type. If the type /// is not registered to the schema the field extension is queued for when it is added (if ever). /// </summary> /// <param name="extension">The extension to add.</param> private void AddTypeExtension(IGraphTypeFieldTemplate extension) { var fieldMaker = new GraphFieldMaker(this.Schema); var fieldResult = fieldMaker.CreateField(extension); if (fieldResult != null) { this.Schema.KnownTypes.EnsureGraphFieldExtension(extension.SourceObjectType, fieldResult.Field); this.EnsureDependents(fieldResult); } }
/// <summary> /// Inspects the root and ensures that any intermediate, virtual fields /// are accounted for and returns a reference to the immediate parent this action should be added to. /// </summary> /// <param name="action">The action.</param> /// <returns>IGraphField.</returns> private IObjectGraphType AddOrRetrieveControllerRoutePath(IGraphTypeFieldTemplate action) { var pathSegments = action.Route.GenerateParentPathSegments(); // loop through all parent path parts of this action // creating virtual fields as necessary or using existing ones and adding on to them IObjectGraphType parentType = this.Schema.OperationTypes[action.Route.RootCollection]; for (var i = 0; i < pathSegments.Count; i++) { var segment = pathSegments[i]; var formattedName = _formatter.FormatFieldName(segment.Name); if (parentType.Fields.ContainsKey(formattedName)) { var field = parentType[formattedName]; var foundType = Schema.KnownTypes.FindGraphType(field.TypeExpression.TypeName); var ogt = foundType as IObjectGraphType; if (ogt != null) { if (ogt.IsVirtual) { parentType = ogt; continue; } throw new GraphTypeDeclarationException( $"The action '{action.Route}' attempted to nest itself under the {foundType.Kind} graph type '{foundType.Name}', which is returned by " + $"the route '{field.Route}'. Actions can only be added to virtual graph types created by their parent controller."); } if (foundType != null) { throw new GraphTypeDeclarationException( $"The action '{action.Route.Path}' attempted to nest itself under the graph type '{foundType.Name}'. {foundType.Kind} graph types cannot " + "accept fields."); } else { throw new GraphTypeDeclarationException( $"The action '{action.Route.Path}' attempted to nest itself under the field '{field.Route}' but no graph type was found " + "that matches its type."); } } parentType = this.CreateVirtualFieldOnParent( parentType, formattedName, segment, i == 0 ? action.Parent : null); } return(parentType); }
/// <summary> /// Iterates the given <see cref="ControllerActionGraphFieldTemplate" /> and adds /// all found types to the type system for this <see cref="ISchema" />. Generates /// a field reference on the provided parent with a resolver pointing to the provided graph action. /// </summary> /// <param name="parentField">The parent which will own the generated action field.</param> /// <param name="action">The action.</param> private void AddActionAsField(IObjectGraphType parentField, IGraphTypeFieldTemplate action) { // apend the action as a field on the parent var maker = new GraphFieldMaker(this.Schema); var fieldResult = maker.CreateField(action); if (fieldResult != null) { parentField.Extend(fieldResult.Field); this.EnsureDependents(fieldResult); } }
/// <summary> /// Creates a single graph field from the provided template using hte rules of this maker and the contained schema. /// </summary> /// <param name="template">The template to generate a field from.</param> /// <returns>IGraphField.</returns> public GraphFieldCreationResult CreateField(IGraphTypeFieldTemplate template) { var formatter = _schema.Configuration.DeclarationOptions.GraphNamingFormatter; var result = new GraphFieldCreationResult(); // if the owner of this field declared top level objects append them to the // field for evaluation var securityGroups = new List <FieldSecurityGroup>(); if (template.Parent?.SecurityPolicies?.Count > 0) { securityGroups.Add(template.Parent.SecurityPolicies); } if (template.SecurityPolicies?.Count > 0) { securityGroups.Add(template.SecurityPolicies); } MethodGraphField field = this.InstantiateField(formatter, template, securityGroups); field.Description = template.Description; field.IsDeprecated = template.IsDeprecated; field.DeprecationReason = template.DeprecationReason; field.Complexity = template.Complexity; field.FieldSource = template.FieldSource; if (template.Arguments != null) { var argumentMaker = new GraphArgumentMaker(_schema); foreach (var argTemplate in template.Arguments) { var argumentResult = argumentMaker.CreateArgument(argTemplate); field.Arguments.AddArgument(argumentResult.Argument); result.MergeDependents(argumentResult); } } result.AddDependentRange(template.RetrieveRequiredTypes()); if (template.UnionProxy != null) { var unionMaker = new UnionGraphTypeMaker(_schema); result.AddDependent(unionMaker.CreateGraphType(template.UnionProxy, template.Kind)); } result.Field = field; return(result); }
/// <summary> /// Adds the <see cref="ControllerActionGraphFieldTemplate"/> to the schema. Any required parent fields /// will be automatically created if necessary to ensure proper nesting. /// </summary> /// <param name="action">The action to add to the schema.</param> private void AddAction(IGraphTypeFieldTemplate action) { if (this.Schema.Configuration.DeclarationOptions.AllowedOperations.Contains(action.Route.RootCollection)) { this.EnsureGraphOperationType(action.Route.RootCollection); var parentField = this.AddOrRetrieveControllerRoutePath(action); this.AddActionAsField(parentField, action); } else { throw new ArgumentOutOfRangeException( nameof(action), $"The '{action.InternalFullName}' action's operation root ({action.Route.RootCollection}) is not " + $"allowed by the schema's current configuration (Schema: {this.Schema.Name})."); } }
/// <summary> /// Adds the <see cref="ControllerActionGraphFieldTemplate"/> to the schema. Any required parent fields /// will be automatically created if necessary to ensure proper nesting. /// </summary> /// <param name="action">The action to add to the schema.</param> private void AddAction(IGraphTypeFieldTemplate action) { switch (action.Route.RootCollection) { case GraphCollection.Query: case GraphCollection.Mutation: this.EnsureGraphOperationType(action.Route.RootCollection); var parentField = this.AddOrRetrieveRoutePath(action); this.AddActionAsField(parentField, action); break; default: throw new ArgumentOutOfRangeException( nameof(action), $"The '{action.InternalFullName}' action's operation root ({action.Route.RootCollection}) is not " + $"supported."); } }
/// <summary> /// Iterates the given <see cref="ControllerActionGraphFieldTemplate" /> and adds /// all found types to the type system for this <see cref="ISchema" />. Generates /// a field reference on the provided parent with a resolver pointing to the provided graph action. /// </summary> /// <param name="parentType">The parent which will own the generated action field.</param> /// <param name="action">The action.</param> private void AddActionAsField(IObjectGraphType parentType, IGraphTypeFieldTemplate action) { // apend the action as a field on the parent var maker = GraphQLProviders.GraphTypeMakerProvider.CreateFieldMaker(this.Schema); var fieldResult = maker.CreateField(action); if (fieldResult != null) { if (parentType.Fields.ContainsKey(fieldResult.Field.Name)) { throw new GraphTypeDeclarationException( $"The '{parentType.Kind}' graph type '{parentType.Name}' already contains a field named '{fieldResult.Field.Name}'. " + $"The action method '{action.InternalFullName}' cannot be added to the graph type with the same name."); } parentType.Extend(fieldResult.Field); this.EnsureDependents(fieldResult); } }
/// <summary> /// Instantiates the graph field according to the data provided. /// </summary> /// <param name="formatter">The formatter.</param> /// <param name="template">The template.</param> /// <param name="securityGroups">The security groups.</param> /// <returns>MethodGraphField.</returns> protected override MethodGraphField InstantiateField( GraphNameFormatter formatter, IGraphTypeFieldTemplate template, List <FieldSecurityGroup> securityGroups) { var subTemplate = template as ControllerSubscriptionActionGraphFieldTemplate; if (subTemplate != null && subTemplate.FieldSource == GraphFieldTemplateSource.Action && subTemplate.Route.RootCollection == GraphCollection.Subscription) { return(new SubscriptionMethodGraphField( formatter.FormatFieldName(template.Name), template.TypeExpression.CloneTo(formatter.FormatGraphTypeName(template.TypeExpression.TypeName)), template.Route, template.Mode, template.CreateResolver(), securityGroups, subTemplate.EventName)); } return(base.InstantiateField(formatter, template, securityGroups)); }
/// <summary> /// Inspects the root and ensures that any intermediate, virtual fields /// are accounted for and returns a reference to the immediate parent this action should be added to. /// </summary> /// <param name="action">The action.</param> /// <returns>IGraphField.</returns> private IObjectGraphType AddOrRetrieveRoutePath(IGraphTypeFieldTemplate action) { var pathSegments = action.Route.GenerateParentPathSegments(); // loop through all parent path parts of this action // creating virtual fields as necessary or using existing ones and adding on to them IObjectGraphType parentType = this.Schema.OperationTypes[action.Route.RootCollection]; for (var i = 0; i < pathSegments.Count; i++) { var segment = pathSegments[i]; var formattedName = _formatter.FormatFieldName(segment.Name); if (parentType.Fields.ContainsKey(formattedName)) { var field = parentType[formattedName]; var foundType = Schema.KnownTypes.FindGraphType(field.TypeExpression.TypeName); if (foundType is IObjectGraphType ogt) { parentType = ogt; continue; } throw new GraphTypeDeclarationException( $"The action '{action.Name}' attempted to nest itself under the grpah type '{foundType?.Name}' but the graph type " + "does not exist or does not accept fields."); } var fieldType = this.CreateVirtualFieldOnParent( parentType, formattedName, segment, i == 0 ? action.Parent : null); parentType = fieldType; } return(parentType); }
/// <summary> /// Creates a single graph field from the provided template using hte rules of this maker and the contained schema. /// </summary> /// <param name="template">The template to generate a field from.</param> /// <returns>IGraphField.</returns> public GraphFieldCreationResult CreateField(IGraphTypeFieldTemplate template) { var formatter = _schema.Configuration.DeclarationOptions.GraphNamingFormatter; var result = new GraphFieldCreationResult(); // if the owner of this field declared top level objects append them to the // field for evaluation var securityGroups = new List <FieldSecurityGroup>(); if (template.Parent?.SecurityPolicies?.Count > 0) { securityGroups.Add(template.Parent.SecurityPolicies); } if (template.SecurityPolicies?.Count > 0) { securityGroups.Add(template.SecurityPolicies); } MethodGraphField field = null; switch (template.FieldSource) { case GraphFieldTemplateSource.Method: case GraphFieldTemplateSource.Action: field = new MethodGraphField( formatter.FormatFieldName(template.Name), template.TypeExpression.CloneTo(formatter.FormatGraphTypeName(template.TypeExpression.TypeName)), template.Route, template.Mode, template.CreateResolver(), securityGroups); break; case GraphFieldTemplateSource.Property: field = new PropertyGraphField( formatter.FormatFieldName(template.Name), template.TypeExpression.CloneTo(formatter.FormatGraphTypeName(template.TypeExpression.TypeName)), template.Route, template.DeclaredReturnType, template.DeclaredName, template.Mode, template.CreateResolver(), securityGroups); break; } field.Description = template.Description; field.IsDeprecated = template.IsDeprecated; field.DeprecationReason = template.DeprecationReason; field.Complexity = template.Complexity; field.FieldSource = template.FieldSource; if (template.Arguments != null) { var argumentMaker = new GraphArgumentMaker(_schema); foreach (var argTemplate in template.Arguments) { var argumentResult = argumentMaker.CreateArgument(argTemplate); field.Arguments.AddArgument(argumentResult.Argument); result.MergeDependents(argumentResult); } } result.AddDependentRange(template.RetrieveRequiredTypes()); if (template.UnionProxy != null) { var unionMaker = new UnionGraphTypeMaker(_schema); result.AddDependent(unionMaker.CreateGraphType(template.UnionProxy, template.Kind)); } result.Field = field; return(result); }