Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delegate Directive not being assigned correctly with ITypeRewriter #766

Closed
kyrim opened this issue May 20, 2019 · 4 comments · Fixed by #786
Closed

Delegate Directive not being assigned correctly with ITypeRewriter #766

kyrim opened this issue May 20, 2019 · 4 comments · Fixed by #786
Assignees
Milestone

Comments

@kyrim
Copy link

kyrim commented May 20, 2019

Describe the bug
We have a use case where we have an API Gateway that will be stitching multiple microservices into one schema. As part of this, we would like to automatically remove certain field arguments (based on a naming convention, in the future potentially a marker directive) from the microservice schema and automatically inject these fields using ContextData from the API Gateway eg:

public class ContextDataMiddleware
{
	private const string ContextUserIdFieldName = "userId";

	// The fields we want to replace with ContextData and not expose
	public static readonly string[] ContextFieldNames = { ContextUserIdFieldName };
	
	public static FieldMiddleware Middleware => next => async context =>
	{
		context.ContextData[ContextUserIdFieldName] = 5;

		await next.Invoke(context);
	};
}

Then rewrite any field with that argument ("userId") with a delegation directive and remove the field from the exposed API eg.

public class ContextDataArgumentRewriter : ITypeRewriter
{
	private readonly string _schema;
	private readonly HashSet<string> _contextDataFields;

	public ContextDataArgumentRewriter(string schema, params string[] contextDataFields)
	{
		_schema = schema;
		_contextDataFields = new HashSet<string>(contextDataFields);
	}

	public ITypeDefinitionNode Rewrite(ISchemaInfo schema, ITypeDefinitionNode typeDefinition)
	{
		if (!(typeDefinition is ObjectTypeDefinitionNode objectTypeDefinition)) return typeDefinition;
	
		var fields = new List<FieldDefinitionNode>();
		
		foreach (var field in objectTypeDefinition.Fields)
		{
			var contextDataArguments = field.Arguments.Where(x => _contextDataFields.Contains(x.Name.Value)).ToList();
			if (!contextDataArguments.Any())
			{
				fields.Add(field);
				continue;
			}

			var normalArguments = field.Arguments.Except(contextDataArguments).ToList();

			var normalArgumentNodes = 
				normalArguments.Select(
					x => new ArgumentNode(x.Name, new ScopedVariableNode(
						null,
						new NameNode(ScopeNames.Arguments),
						x.Name)));
			
			var contextDataArgumentNodes = 
				contextDataArguments.Select(
					x => new ArgumentNode(x.Name, new ScopedVariableNode(
						null,
						new NameNode(ScopeNames.ContextData),
						x.Name)));

			var path = new SelectionPathComponent(
				field.Name, 
				normalArgumentNodes.Concat(contextDataArgumentNodes).ToList()
			);

			fields.Add(
				field
					// Doesn't seem to add the delegate directive in the resultant schema
					.AddDelegationPath(_schema, path)
					// Ensure we exclude these fields we are now filling
					.WithArguments(normalArguments)
			);
		}
		
		return objectTypeDefinition.WithFields(fields);
	}
}

Even after using the new AddDelegationPath method:

...
field.AddDelegationPath(_schema, path)
...

The resultant schema does not seem to have the field with the directive applied, and does not show in GraphQL Playground / Graphiql (may be a seperate issue?):
image

Running a query where we expect the field to be filled with ContextData and sent to the microservice does not seem to work.

To Reproduce
Please see example project: schema-stitch-test (1).zip

Run both the ApiGateway and Microservice projects concurrently.

Notice Microservice has a "userId" argument, and ApiGateway does not, for the "userFeed" field.

Run a query like:

{
  userFeed(limit: 5) {
    cursor
  }
}

Notice you will receive an error: ""The argument userId is required and does not allow null values." and the resultant schema does show the delegate on the field argument.

Expected behavior
The Delegate directive should be applied and queries where the delegate is used should automatically fill with the context data when sent to the stitched service.

Desktop (please complete the following information):

  • OS: Windows 10, MacOS
  • Version: v9.0.0-preview.36

Additional context
As discussed on slack here.

@kyrim
Copy link
Author

kyrim commented May 20, 2019

As a side note, I don't think custom directives are outputted on fields/arguments via introspection as per:
graphql/graphql-spec#300, that might be why Playground/ graphiql are not showing the delegate directive there.

@michaelstaib
Copy link
Member

As a side note, I don't think custom directives are outputted on fields/arguments via introspection as per: ...

They are not. But you are able to export the HC schema as SDL and then import that into the stitching layer. Just if you want to go that route.

Also, with V10 we will provide a schema registry that will have those information.

@michaelstaib michaelstaib added this to the 9.0.0 milestone May 20, 2019
@michaelstaib michaelstaib self-assigned this May 20, 2019
@michaelstaib
Copy link
Member

We got this working now... the default rules where overwriting the custom type rewriters.

@kyrim
Copy link
Author

kyrim commented May 24, 2019

Just tested, works perfectly! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants