question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Implementing an @export directive

See original GitHub issue

I want to implement an @export directive. This would export the value of a field into a variable that can be used in another part of the query (discussed here and here). I have read the documentation about implementing a directive, from here.

Here is an example of how this would work:

query($cid : Int!, $ano : Int!, $customerId : Int)
{
  useCompany(no: $cid) 
  {
    associate(filter : {associateNo : {_eq: $ano}})
    {
      items 
      {
        customerNo @export(as: "customerId")
      }
    }
    
    order(first : 5, filter : {customerNo : {_eq : $customerId}})
    {
      items
      {
        orderNo
        orderDate
        customerNo
      }
    }
  }
}

Here, I first execute a query that retrieves a customer ID and then this is used in a second query to fetch some orders of that customer.

I defined a directive as follows:

   internal class ExportDirective : Directive
   {
      public ExportDirective() : 
         base("export", DirectiveLocation.Field, DirectiveLocation.FragmentSpread, DirectiveLocation.InlineFragment)
      {
         Description = "Export the value of a field into a variable that can be used somewhere else in the query.";
         Arguments = new QueryArguments(
            new QueryArgument<StringGraphType>
            {
               Name = "as",
               Description = "The name of the exported variable"
            });
      }
   }

And a visitor as follows:

   internal class ExportDirectiveVisitor : BaseSchemaNodeVisitor
   {
      public override void VisitObjectFieldDefinition(FieldType field, IObjectGraphType type, ISchema schema)
      {
         var applied = field.FindAppliedDirective("export");
         if (applied != null)
         {
            var inner = field.Resolver ?? NameFieldResolver.Instance;
            field.Resolver = new FuncFieldResolver<object>(async context =>
            {
               object result = await inner.ResolveAsync(context);

               var arg = applied.FindArgument("as");
               var exportName = arg?.Name;

               if (!string.IsNullOrEmpty(exportName))
               {
                  var value = context.Variables.ValueFor(exportName);
                  if (value is null)
                  {
                     context.Variables.Add(new Variable(exportName) { Value = result });
                  }
               }

               return result;
            });
         }
      }
   }

(Ignore the actual details of this implementation. Might not be correct or complete, but I didn’t get a chance to test it.)

I registered them with the schema:

   public class MySchema : Schema
   {
      public MySchema(IServiceProvider provider)
          : base(provider)
      {
         Directives.Register(new ExportDirective());
         RegisterVisitor(new ExportDirectiveVisitor());
      }
   }

The visitor executes when I start the app and the schema is created. But this is not what I want. I want this to be evaluated every time I run a query. What do I have to do for that?

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:23 (14 by maintainers)

github_iconTop GitHub Comments

2reactions
Shane32commented, Nov 7, 2022

I also don’t quite understand why the delay nodes execute before exhausting the main loop. I had expected your code to execute all the regular nodes first, then all the data loader nodes, then if the prior loops are satisfied, all the delayed nodes. Something like this:

while (nodes.Count > 0 || dataLoaderNodes.Count > 0 || delayedNodes.Count > 0) {
  while (nodes.Count > 0 || dataLoaderNodes.Count > 0) {
    while (nodes.Count > 0) {
      // process regular nodes
    }
    while (dataLoaderNodes.Count > 0) {
      // process data loader nodes
    }
  }
  while (delayedNodes.Count > 0) {
    // move delayed nodes to regular queue
    var node = delayedNodes.Pop();
    nodes.Push(node);
  }
}

You may be able to considerably reduce the amount of code in your class with that logic, if it works correctly.

1reaction
mariusbancilacommented, Nov 7, 2022

Excellent suggestion, the state can be anything here. Yes, I tried it an it works so I can remove some unnecessary code.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Creating an @export GraphQL directive
The goal for an @export directive in GraphQL is to export the value of a field (or set of fields) into a variable...
Read more >
Exporting Using Directives
Exporting Using Directives. Nathan Sidwell. This discusses how exported using directives affect name lookup. 1 Background.
Read more >
FSIS Directive 9000.1 - Export Certification
This directive provides instructions to inspection program personnel (IPP) for performing export certification of meat (including ...
Read more >
export-directive in java - liveBook · Manning
Inside the module block, requires directives express the dependencies between modules, and exports directives define each module's public API by naming the ...
Read more >
export - JavaScript - MDN Web Docs
The export declaration is used to export values from a JavaScript module. Exported values can then be imported into other programs with the ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found