Compiled Experience Windows Platform Development http://compiledexperience.com en Fri, 24 Apr 2020 02:22:26 +0000 Fri, 24 Apr 2020 02:22:26 +0000 Creating and enforcing GraphQL schema conventions <p>When we’re building an API we’re creating a contract between ourselves and our consumers. We should ideally make that contract as easier to understand as possible, this can help reduce support burden on the API team and empower the consumer to do more with the API.</p> <p>GraphQL comes with a strong type system that represents the fields available to query and their types. Collectively this collection of types is known as the <strong>schema</strong> of the service.</p> <p>Typically when design the schema we’ll have a series of conventions or rules for the shape of the schema. Examples of these types of conventions could be:</p> <h3 id="naming">Naming</h3> <ul> <li>Type names should be pascal cased.</li> <li>Field names should be camel cased.</li> <li>Arguments should be camel cased.</li> <li>Interfaces should not have the <code class="language-plaintext highlighter-rouge">I</code> prefix from donet.</li> <li>Types shouldn’t have the <code class="language-plaintext highlighter-rouge">Type</code> suffix.</li> </ul> <h3 id="structure">Structure</h3> <ul> <li>All enums should support the <code class="language-plaintext highlighter-rouge">UNKNOWN</code> value.</li> </ul> <h3 id="security">Security</h3> <ul> <li>All root level mutations are secured against unauthenticated access.</li> </ul> <p>It’s worth nothing that a lot of these conventions may be enforced by your GraphQL framework of choice.</p> <h2 id="enforcing-conventions">Enforcing conventions</h2> <p>Unit tests are probably the easiest way to create and enforce conventions against our schema. If we can express the convention in code then we can easily determine if parts of our schema don’t conform to the convention.</p> <p>The first thing our tests will need to do is create the same schema as our application.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// This method is the schema setup code that would likely be called in ASP.NET Startup</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">CreateSchema</span><span class="p">(</span><span class="n">IServiceCollection</span> <span class="n">services</span><span class="p">)</span> <span class="p">{</span> <span class="n">services</span><span class="p">.</span><span class="nf">AddGraphQL</span><span class="p">(</span> <span class="n">SchemaBuilder</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span> <span class="p">.</span><span class="n">AddQueryType</span><span class="p">&lt;</span><span class="n">QueryType</span><span class="p">&gt;()</span> <span class="p">...</span> <span class="c1">// more setup code here</span> <span class="p">);</span> <span class="p">}</span> <span class="k">static</span> <span class="n">ISchema</span> <span class="nf">CreateSchemaForTesting</span><span class="p">()</span> <span class="p">{</span> <span class="kt">var</span> <span class="n">services</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">ServiceCollection</span><span class="p">();</span> <span class="nf">CreateSchema</span><span class="p">(</span><span class="n">services</span><span class="p">);</span> <span class="kt">var</span> <span class="n">provider</span> <span class="p">=</span> <span class="n">services</span><span class="p">.</span><span class="nf">BuildServiceProvider</span><span class="p">();</span> <span class="k">return</span> <span class="n">provider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p">&lt;</span><span class="n">ISchema</span><span class="p">&gt;();</span> <span class="p">}</span> </code></pre></div></div> <p>With these helper methods in place we can create tests that use linq to expore our collection of types. The following ensures all our enums support <code class="language-plaintext highlighter-rouge">UNKNOWN</code>. One of the reasons we do this is to esnsure the uninitialized variables of database columns don’t automatically hold a business value.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="n">Fact</span><span class="p">]</span> <span class="k">public</span> <span class="k">void</span> <span class="nf">EnumTypesShouldSupportUnknown</span><span class="p">()</span> <span class="p">{</span> <span class="kt">var</span> <span class="n">schema</span> <span class="p">=</span> <span class="nf">CreateSchemaForTesting</span><span class="p">();</span> <span class="kt">var</span> <span class="n">enumsWithoutUnknown</span> <span class="p">=</span> <span class="n">schema</span><span class="p">.</span><span class="n">Types</span> <span class="p">.</span><span class="n">OfType</span><span class="p">&lt;</span><span class="n">EnumType</span><span class="p">&gt;()</span> <span class="p">.</span><span class="nf">Where</span><span class="p">(</span><span class="n">t</span> <span class="p">=&gt;</span> <span class="p">!</span><span class="n">IntrospectionTypes</span><span class="p">.</span><span class="nf">IsIntrospectionType</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">Name</span><span class="p">))</span> <span class="p">.</span><span class="nf">Where</span><span class="p">(</span><span class="n">t</span> <span class="p">=&gt;</span> <span class="n">t</span><span class="p">.</span><span class="n">Values</span><span class="p">.</span><span class="nf">All</span><span class="p">(</span><span class="n">v</span> <span class="p">=&gt;</span> <span class="n">v</span><span class="p">.</span><span class="n">Name</span> <span class="p">!=</span> <span class="s">"UNKNOWN"</span><span class="p">))</span> <span class="p">.</span><span class="nf">ToList</span><span class="p">();</span> <span class="n">Assert</span><span class="p">.</span><span class="nf">Empty</span><span class="p">(</span><span class="n">enumsWithoutUnknown</span><span class="p">);</span> <span class="p">}</span> </code></pre></div></div> <p>One important thing we want to do is remove the introspection types from the mix, otherwise we’d see failures for the enums <code class="language-plaintext highlighter-rouge">__DirectiveLocation</code> and <code class="language-plaintext highlighter-rouge">__TypeKind</code>.</p> <h3 id="adding-conventions-to-an-existing-schema">Adding conventions to an existing schema</h3> <p>Obviously the earlier in a project’s lifecycle we can apply rules and conventions like these the better. This enables us to ensure all our future changes to the schema are inline with our intended direction. Sometimes however this isn’t the case. We may have an existing schema we want to provide a stronger future direction to by using convention based tests such as these.</p> <p>The problem comes when we may have parts of our existing schema that violate our conventions. Ideally we’d update our schema to reflect our conventions but if this is a schema that’s being used in production these updates represent breaking changes to our clients. We may have to go through a migration process to get our clients using the more conventional approach. This would look something like:</p> <ol> <li>Add the new types / fields to the schema, but preserve the existing non-conventional types / fields, mark them as <code class="language-plaintext highlighter-rouge">@deprecrated</code>.</li> <li>Use stats / metrics to determine when none of your clients are using the deprecated types / fields any more.</li> <li>Remove the deprecated types / fields.</li> </ol> <p>When we’re going through this process we still want to have our conventions enforced for new changes but we capture these existing types as “exceptions to the rule” until we’ve managed to migrate our users away from them.</p> <p>This is where <a href="https://compiledexperience.com/blog/posts/approval-tests">Approval Tests</a> come in, if we update our test to replace the <code class="language-plaintext highlighter-rouge">Assert</code> with the following we can capture all the existing broken conventions in our approval file.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Approvals</span><span class="p">.</span><span class="nf">VerifyAll</span><span class="p">(</span><span class="s">"All enums should support an UNKNOWN value."</span><span class="p">,</span> <span class="n">enumsWithoutUnknown</span><span class="p">,</span> <span class="n">t</span> <span class="p">=&gt;</span> <span class="n">t</span><span class="p">.</span><span class="n">Name</span><span class="p">);</span> </code></pre></div></div> <p>New types that break the convention will cause the approval to fail and as we work through the migration we can update the approval file. Once we have no existing types against the convention we can update the test to our <code class="language-plaintext highlighter-rouge">Assert</code>.</p> <h2 id="conclusion">Conclusion</h2> <p>We can use unit tests to explore our GraphQL schema to ensure it follows conventions and approaches we’ve decided up when designing our schema.</p> <p>We can then use tools such as apporval tests to capture existsing broken conventions when applying these rules to existing schemas while working to remove them.</p> Thu, 23 Apr 2020 00:00:00 +0000 http://compiledexperience.com/blog/posts/enforcing-schema-conventions http://compiledexperience.com/blog/posts/enforcing-schema-conventions nigel.sampson@compiledexperience.com (Nigel Sampson) csharp graphql Organizing large schemas in GraphQL <p>A GraphQL service is made up for two parts, a schema and a collection of resolvers. The approaches for defining the schema will depend a lot on the capabilities library / framework you’re using to build the server. In general terms we can broadly categorise in to two categories <strong>schema-first</strong> and <strong>code-first</strong>.</p> <h3 id="schema-first">Schema first</h3> <p>This is where the schema is defined using the SDL (Schema Definition Language) that would look something like the following:</p> <div class="language-graphql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span><span class="w"> </span><span class="n">Query</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">product</span><span class="p">(</span><span class="n">id</span><span class="p">:</span><span class="w"> </span><span class="nb">ID</span><span class="p">!):</span><span class="w"> </span><span class="n">Product</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">type</span><span class="w"> </span><span class="n">Product</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">id</span><span class="p">:</span><span class="w"> </span><span class="nb">ID</span><span class="p">!</span><span class="w"> </span><span class="n">name</span><span class="p">:</span><span class="w"> </span><span class="nb">String</span><span class="p">!</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>This SDL is often declared as a const string in code or in a file that’s read in. Schema first is the more common approach you’ll see in blog posts and tutorials as it can be easier to follow along.</p> <h3 id="code-first">Code first</h3> <p>A number of libraries support defining your schema using objects within code, depending on how smart the library is the schema may be able to be inferred from existing classes in your code base. An example using <a href="https://hotchocolate.io/">Hot Chocolate</a> looks like the following:</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">QueryType</span> <span class="p">:</span> <span class="n">ObjectType</span><span class="p">&lt;</span><span class="n">Query</span><span class="p">&gt;</span> <span class="p">{</span> <span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Configure</span><span class="p">(</span><span class="n">IObjectTypeDescriptor</span><span class="p">&lt;</span><span class="n">QueryType</span><span class="p">&gt;</span> <span class="n">descriptor</span><span class="p">)</span> <span class="p">{</span> <span class="n">descriptor</span><span class="p">.</span><span class="nf">Field</span><span class="p">(</span><span class="n">q</span> <span class="p">=&gt;</span> <span class="n">q</span><span class="p">.</span><span class="nf">Product</span><span class="p">(</span><span class="k">default</span><span class="p">!,</span> <span class="k">default</span><span class="p">!))</span> <span class="p">.</span><span class="nf">Argument</span><span class="p">(</span><span class="s">"id"</span><span class="p">,</span> <span class="n">a</span> <span class="p">=&gt;</span> <span class="n">a</span><span class="p">.</span><span class="n">Type</span><span class="p">&lt;</span><span class="n">NonNullType</span><span class="p">&lt;</span><span class="n">IdType</span><span class="p">&gt;&gt;())</span> <span class="p">.</span><span class="n">Type</span><span class="p">&lt;</span><span class="n">ProductType</span><span class="p">&gt;()</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>This approach isn’t as easy to get started with as you need to learn the particulars of the library in question. Schema defined as code has some major benefits if you’re building a number of GraphQL services and wish to share a common type system between them.</p> <h3 id="other-approaches">Other approaches</h3> <p>Thie choice between code first and schema first is not a binary one, other libraries may have different approaches or even allow a mix of approaches. One of the services at <a href="https://pushpay.com">Pushpay</a> define their schema using a schema first approach but augment it with common types from code.</p> <h2 id="scaling-up-prolems">Scaling up prolems</h2> <p>All of these approaches have thier pros and cons and it’s not the point of this post to debate them. Some of the decision on which approach to use will be subjective as well.</p> <p>What’s really interesting is with each approach how we deal with management of the schema as the service grows in complexity and the amount of types keeps increasing.</p> <h3 id="schema-first-1">Schema first</h3> <p>If we have our schema defined in a single SDL file than as our types increase the file can grow to a unmanagable size. This leads to merge conflcts as different developers all merge to the same file as well as other problems.</p> <p>Thankfully most libraries support defining a schema via a <code class="language-plaintext highlighter-rouge">string</code> as well as a file. This means a very simplistic approach can be to break the schema into smaller files (I’d base the split around DDD aggregate roots). We can then read each file, concatenate the results and use this as our final schema.</p> <p>This solves most our problems but their are natural points where these types merge, especially at the root types (usually named <code class="language-plaintext highlighter-rouge">Query</code>, <code class="language-plaintext highlighter-rouge">Mutation</code> and <code class="language-plaintext highlighter-rouge">Subscription</code>). You can have multiple definitions of these types across the files and grouping the definition in one file breaks the idea of each file being relatively self contained.</p> <p>Thankfully GraphQL has the <code class="language-plaintext highlighter-rouge">extend</code> keyword we can use to solve this problem. We may start with a <code class="language-plaintext highlighter-rouge">shell.gql</code> file that defines an empty <code class="language-plaintext highlighter-rouge">Query</code> type.</p> <div class="language-graphql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span><span class="w"> </span><span class="n">Query</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>Following that our <code class="language-plaintext highlighter-rouge">products.gql</code> can then extend <code class="language-plaintext highlighter-rouge">Query</code> and add new fields.</p> <div class="language-graphql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">extend</span><span class="w"> </span><span class="k">type</span><span class="w"> </span><span class="n">Query</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">product</span><span class="p">(</span><span class="n">id</span><span class="p">:</span><span class="w"> </span><span class="nb">ID</span><span class="p">!):</span><span class="w"> </span><span class="n">Product</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>This gives use nice self contained subsections of our schema that can then be combined to build a full schema.</p> <h3 id="code-first-1">Code first</h3> <p>The code first approach suffers less from the problems as scale as most developers naturally break their classes into separate files. If you have a lot of types you may want break them up into namespaces / folders along the same lines as I described in the schema first approach.</p> <p>And just like schema first we have the same collision points around the <code class="language-plaintext highlighter-rouge">Query</code>, <code class="language-plaintext highlighter-rouge">Mutation</code> and <code class="language-plaintext highlighter-rouge">Subscription</code> types. Whether the same solution works will depend a bit on the library, thenkfully <a href="https://hotchocolate.io/">Hot Chocolate</a> supports defining object extensions in code first. We may start with a empty <code class="language-plaintext highlighter-rouge">Query</code> type</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">QueryType</span> <span class="p">:</span> <span class="n">ObjectType</span><span class="p">&lt;</span><span class="n">Query</span><span class="p">&gt;</span> <span class="p">{</span> <span class="p">}</span> </code></pre></div></div> <p>and then use extension types in the namespaces</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">ProductsQueryTypeExtension</span> <span class="p">:</span> <span class="n">ObjectTypeExtension</span> <span class="p">{</span> <span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Configure</span><span class="p">(</span><span class="n">IObjectTypeDescriptor</span><span class="p">&lt;</span><span class="n">QueryType</span><span class="p">&gt;</span> <span class="n">descriptor</span><span class="p">)</span> <span class="p">{</span> <span class="n">descriptor</span><span class="p">.</span><span class="nf">Name</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">Query</span><span class="p">));</span> <span class="n">descriptor</span><span class="p">.</span><span class="nf">Field</span><span class="p">(</span><span class="n">q</span> <span class="p">=&gt;</span> <span class="n">q</span><span class="p">.</span><span class="nf">Product</span><span class="p">(</span><span class="k">default</span><span class="p">!,</span> <span class="k">default</span><span class="p">!))</span> <span class="p">.</span><span class="nf">Argument</span><span class="p">(</span><span class="s">"id"</span><span class="p">,</span> <span class="n">a</span> <span class="p">=&gt;</span> <span class="n">a</span><span class="p">.</span><span class="n">Type</span><span class="p">&lt;</span><span class="n">NonNullType</span><span class="p">&lt;</span><span class="n">IdType</span><span class="p">&gt;&gt;())</span> <span class="p">.</span><span class="n">Type</span><span class="p">&lt;</span><span class="n">ProductType</span><span class="p">&gt;()</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <h2 id="conclusion">Conclusion</h2> <p>Regardless of the approach we use to define the schema using extensions allows us to break up the schema in ways that will become manageable throughout your development.</p> Tue, 24 Mar 2020 00:00:00 +0000 http://compiledexperience.com/blog/posts/organizing-schemas http://compiledexperience.com/blog/posts/organizing-schemas nigel.sampson@compiledexperience.com (Nigel Sampson) csharp graphql Upcoming Speaking Events <p>I have a couple of upcoming speaking events I’d like to let you all know about.</p> <p>The first is <a href="https://www.fullstackday.com/2020/">Full Stack Days</a> on March 19th here in Auckland, New Zealand. I’ll be giving a relatively technology agnostic view on GraphQL, it’s strength and weaknesses as well as a general overview. If you want to find out about the topic and whether it’s worth your time looking further into then this is the talk for you.</p> <p>The second is <a href="https://codemania.io/">Codemania</a> on May 6th again here in Auckland. Codemania has always been one of my favourite conferences to attend and I’m really honoured to have the opportunity to speak there. This talk will be a longer one and cover a lot of the learnings we’ve made at <a href="https://pushpay.com/">Pushpay</a> over the last few years with GraphQL including Schema Stitching and Operations. If you’re wanting to learning more about GraphQL as it fits into the micro-service landscape then this should be the talk for you.</p> Mon, 24 Feb 2020 00:00:00 +0000 http://compiledexperience.com/blog/posts/upcoming-speaking http://compiledexperience.com/blog/posts/upcoming-speaking nigel.sampson@compiledexperience.com (Nigel Sampson) Nord theme for Windows Terminal <p>I’ve been playing around with <a href="https://github.com/microsoft/terminal">Windows Terminal</a> for the last few weeks and been really impressed. In prevoius terminals and in VS Code lately I’ve had the <a href="https://www.nordtheme.com/">Nord theme</a> which while totally subjective looks pretty cool.</p> <p>It has <a href="https://www.nordtheme.com/ports">ports</a> for a lot of applications, but not Windows Terminals. Thankfully Anais Betts put together an awesome point on how to <a href="https://blog.anaisbetts.org/vs-code-themes-in-windows-terminal/">convert a VS Code theme to a Windows Terminal</a>.</p> <p>For anyone else who’s keen there it is.</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"name"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"nord"</span><span class="p">,</span><span class="w"> </span><span class="nl">"background"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"#2e3440"</span><span class="p">,</span><span class="w"> </span><span class="nl">"foreground"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"#d8dee9"</span><span class="p">,</span><span class="w"> </span><span class="nl">"black"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#3b4252"</span><span class="p">,</span><span class="w"> </span><span class="nl">"blue"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#81a1c1"</span><span class="p">,</span><span class="w"> </span><span class="nl">"brightBlack"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#4c566a"</span><span class="p">,</span><span class="w"> </span><span class="nl">"brightBlue"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#81a1c1"</span><span class="p">,</span><span class="w"> </span><span class="nl">"brightCyan"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#8fbcbb"</span><span class="p">,</span><span class="w"> </span><span class="nl">"brightGreen"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#a3be8c"</span><span class="p">,</span><span class="w"> </span><span class="nl">"brightPurple"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#b48ead"</span><span class="p">,</span><span class="w"> </span><span class="nl">"brightRed"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#bf616a"</span><span class="p">,</span><span class="w"> </span><span class="nl">"brightWhite"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#eceff4"</span><span class="p">,</span><span class="w"> </span><span class="nl">"brightYellow"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#ebcb8b"</span><span class="p">,</span><span class="w"> </span><span class="nl">"cyan"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#88c0d0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"green"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#a3be8c"</span><span class="p">,</span><span class="w"> </span><span class="nl">"purple"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#b48ead"</span><span class="p">,</span><span class="w"> </span><span class="nl">"red"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#bf616a"</span><span class="p">,</span><span class="w"> </span><span class="nl">"white"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#e5e9f0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"yellow"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#ebcb8b"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> Thu, 05 Dec 2019 00:00:00 +0000 http://compiledexperience.com/blog/posts/windows-terminal-nord http://compiledexperience.com/blog/posts/windows-terminal-nord nigel.sampson@compiledexperience.com (Nigel Sampson) Slides and demos for .NET Conf 2019 <p>Last week I had the incredible opportunity to speak at <a href="https://www.dotnetconf.net/">.NET Conf</a> on <em>An Introduction to GraphQL in .NET Core</em>. It was certainly an novel experience to giving a talk from an empty Pushpay meeting room to an unknown amount of people.</p> <p>For anyone who would like to see the talk after the fact it’s currently on <a href="https://www.twitch.tv/videos/486001703">Twitch</a> at around the 3:10 mark. I believe the individual talks will be on YouTube later on, I’ll update this post when they do.</p> <p>The <a href="https://github.com/dotnet-presentations/dotnetconf2019">slides</a> and <a href="https://github.com/nigel-sampson/talks">demos</a> are available on GitHub for your perusal as well.</p> Mon, 30 Sep 2019 00:00:00 +0000 http://compiledexperience.com/blog/posts/dotnetconf-2019-slides http://compiledexperience.com/blog/posts/dotnetconf-2019-slides nigel.sampson@compiledexperience.com (Nigel Sampson) csharp graphql Speaking at .NET Conf 2019 <p>I’m pleased to announce that I’ll be speaking in a few weeks at the Microsoft virtual conference <a href="https://www.dotnetconf.net/">.NET Conf</a>! This looks like it’ll be a great conference that covers a wide range of subjects and also includes the launch of .NET Core 3.0.</p> <p>Personally I’ll be speaking on <em>An Introduction to GraphQL in .NET Core</em>, I’ve found a majority of GraphQL 101 style talks are heavily focussed around the Node ecosystem. In fact a lot of discussions around the limitations of a specifics such as schema stitching are more about certainl implementations rather than the appoach in general (but that’s another rant). Given this I’m very keen to be able to start with an introduction to using GraphQl with examples of building a service using .NET Core and in particular the <a href="https://github.com/ChilliCream/hotchocolate">Hot Chocolate</a> library.</p> <p>Check out the <a href="https://www.dotnetconf.net/agenda">agenda</a> and see what piques your interest.</p> Mon, 09 Sep 2019 00:00:00 +0000 http://compiledexperience.com/blog/posts/dotnetconf-2019 http://compiledexperience.com/blog/posts/dotnetconf-2019 nigel.sampson@compiledexperience.com (Nigel Sampson) csharp graphql Approval Tests <p>Approval Tests or Snapshot Tests are in my opinion a vital part of the testing ecosystem that can service a number of uses cases. In this post I’ll introduce the concepts and discuss some of the scenarios we may want to employ them to best effect.</p> <h2 id="what-are-approval-tests">What are approval tests?</h2> <p>Approval tests work by creating an output that needs human approval / verification. This could be because of a number of reasons:</p> <ul> <li>The output is visual such as a screenshot.</li> <li>The output is too large for regular unit testing assertions (a complex object graph etc.).</li> <li>The output has no way to programmatically defined.</li> </ul> <p>Once the initial output has been defined and “approved” then as long as the test provides consistent output then the test will continue to pass. Once the test provides output that is different to the approved output then the test will fail. The developer then has two choices:</p> <ol> <li>If the change in the output was unintended then fix the bug that’s causing the change in the output.</li> <li>“Approve” the new output as the baseline for future tests.</li> </ol> <p>I’ve been very vague here about “output” intentionally because that output can be anything you want really, as long as it can be compared to another copy in a consistent manner.</p> <h2 id="when-should-i-use-approval-tests">When should I use approval tests?</h2> <p>Here are some scenarios where we use approval tests at <a href="https://pushpay.com">Pushpay</a>.</p> <h3 id="website-visuals">Website Visuals</h3> <p>We use an automated testing process that takes screenshots (using headless Chrome) of most pages in our web applications with static data. If the page has changed for any reason then the test fails. If that change is intended the developer can simply mark the new visual as approved and carry on, otherwise it gives a clear indidication that the changes they’re testing are having unintended consequences on pages they didn’t attend.</p> <h3 id="api-contracts">API Contracts</h3> <p>We use the SDL of GraphQL API’s and Swagger for the REST API’s as the output for an approval test. What this means is that any change that results in the change to the public API contract will result in a failed test. We want to be very deliberate in any changes we make in the contracts we expose to other teams and this guards against unintended changes. It also forces the developer to think very closely about the contract as they approve it.</p> <h3 id="exceptions-to-the-rule">Exceptions to the rule</h3> <p>We may have a conventions or policies that will almost always have exceptions. An example of this are our feature flags, ideally our Sandbox and Production environments should be very similar in terms of which flags are on. We can have an approval test where the output is the list of all flags where the state is different between environments, this way we can capture and approve exceptions to our rule and ensure that any of these exceptions are intentional and approved.</p> <h2 id="approval-tests-in-net">Approval Tests in .NET</h2> <p>Some good frameworks I’ve used for approval tests in .NET are <a href="https://github.com/approvals/ApprovalTests.Net">Approval Tests.NET</a> and <a href="https://github.com/SwissLife-OSS/snapshooter">Snapshooter</a>.</p> <h2 id="summary">Summary</h2> <p>I hope you can see from the examples above that approval tests fill a very valuable niche where the output of the test requires human intelligence to assert corrections. They can capture impossible to assert rules as well as exceptions to the rules. They can help to ensure that changes to critical parts of your system such as the API and the visuals are intentional and approved.</p> Tue, 30 Jul 2019 00:00:00 +0000 http://compiledexperience.com/blog/posts/approval-tests http://compiledexperience.com/blog/posts/approval-tests nigel.sampson@compiledexperience.com (Nigel Sampson) csharp Automatically validate arguments in GraphQL <p>In my last post on <a href="/blog/posts/grapql-validation-metadata">Exposing Validation Metadata in GraphQL</a> I showed how we can expose validation metadata from the server to the client in order to have to only define our validation rules once. In this post I’ll show how to autoamically enforce those validation rules on the server.</p> <p>Having validation rules defined doesn’t mean much if we don’t enforce them. In when using MVC this was done through the <code class="language-plaintext highlighter-rouge">ModelState</code> property on the <code class="language-plaintext highlighter-rouge">Controller</code>. You’d regularly see code in the following pattern in your controllers.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="n">IActionResult</span> <span class="nf">CreatePerson</span><span class="p">(</span><span class="n">CreatePersonInput</span> <span class="n">input</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(!</span><span class="n">ModelState</span><span class="p">.</span><span class="n">IsValid</span><span class="p">)</span> <span class="k">return</span> <span class="nf">View</span><span class="p">(</span><span class="n">input</span><span class="p">);</span> <span class="p">...</span> <span class="p">}</span> </code></pre></div></div> <p>There’s nothing wrong with this code, but one thing that did bug me was it didn’t feel like the “pit of success”.</p> <blockquote> <p>The Pit of Success: in stark contrast to a summit, a peak, or a journey across a desert to find victory through many trials and surprises, we want our customers to simply fall into winning practices by using our platform and frameworks. To the extent that we make it easy to get into trouble we fail.</p> </blockquote> <p>In this case it’s too easy to forget to add those lines to your contoller and leave validation compltely off your endpoint. We can solve this in MVC by using filters to enforece validation on all our <code class="language-plaintext highlighter-rouge">POST</code> requests.</p> <h2 id="graphql-middleware">GraphQL Middleware</h2> <p>Middleware is a very overloaded term when it comes to software development, within the context of frameworks it typically refers to a pluggable pipeline that an operation moves through. What gets confusing is that each framework typically has it’s own notion of middleware which can make them confusing when discussing them.</p> <p>An example of this is that .NET Core has a concept of middleware for any <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware">incoming requests</a>, in fact <a href="https://hotchocolate.io/">Hot Chocolate</a> the GraphQL framework we’re using is implemented as a piece of .NET Core middleware where it examines requests and if it determines that it’s a GraphQL query will execute it.</p> <p><a href="https://hotchocolate.io/">Hot Chocolate</a> also has it’s own <a href="https://hotchocolate.io/docs/middleware">concept of middleware</a>, in fact it has three.</p> <ul> <li>Query Middleware</li> <li>Field Middleware</li> <li>Directive Middleware</li> </ul> <p>These are all similar in concept in that these are a pipeline that is executed as the GraphQL query / mutation is executed. The difference is simply the scope of the middleware and when it’s executed. Let’s examine each in turn and in the context of enforcing validation.</p> <h3 id="query-middleware">Query Middleware</h3> <p>This middleware is executed once per query / mutation and roughly akin to the MVC filters discussed above. For an MVC system this would be the ideal place for validation, but a GraphQL query can have any number of fields with any number of arguments. Implementing validation in query level middleware would involve having to walk to the document to find all the values and so isn’t our best candidate.</p> <h3 id="field-middleware">Field Middleware</h3> <p>This middleware is executed whenever the field the middleware is applied to resolved. This gives us a wide range of options. If we apply the field middleware to the schema directly:</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SchemaBuilder</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span> <span class="p">.</span><span class="n">Use</span><span class="p">&lt;</span><span class="n">ValidateInputMiddleware</span><span class="p">&gt;()</span> <span class="p">.</span><span class="nf">Create</span><span class="p">();</span> </code></pre></div></div> <p>then this middleware will execute for every single field on every single request. This may be desirable in some scenarios but for ours is probably overkill. However if we’re defining our schema in a code first manner we can apply middleware to just the fields we care about:</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">descriptor</span><span class="p">.</span><span class="nf">Field</span><span class="p">(</span><span class="n">m</span> <span class="p">=&gt;</span> <span class="n">m</span><span class="p">.</span><span class="nf">CreatePerson</span><span class="p">(</span><span class="k">default</span><span class="p">))</span> <span class="p">.</span><span class="nf">Argument</span><span class="p">(</span><span class="s">"input"</span><span class="p">,</span> <span class="n">a</span> <span class="p">=&gt;</span> <span class="n">a</span><span class="p">.</span><span class="n">Type</span><span class="p">&lt;</span><span class="n">NonNullType</span><span class="p">&lt;</span><span class="n">CreatePersonInputType</span><span class="p">&gt;&gt;())</span> <span class="p">.</span><span class="n">Use</span><span class="p">&lt;</span><span class="n">ValidateInputMiddleware</span><span class="p">&gt;();</span> </code></pre></div></div> <p>This middleware will only execute when the <code class="language-plaintext highlighter-rouge">createPerson</code> mutation is executed which is very limited in scope, but here we’re back to having to remember to apply it to the fields we want validation on. The former approach is overly broad but automatic, the latter exactly brroad enough but not automatic.</p> <h3 id="directive-middleware">Directive Middleware</h3> <p>The latter approach above use a code first schema definition to apply a piece of middleware to only specifc fields. If we’re using a schema first approach then how can we do something similar? We can leverage directives, I think of these in the same way as C# attributes, pieces of metadata attached to our queries and schema. In <a href="https://hotchocolate.io/">Hot Chocolate</a> directives can apply middleware. If we define one that looks like:</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">ValidateInputDirective</span> <span class="p">{</span> <span class="p">}</span> <span class="k">public</span> <span class="k">class</span> <span class="nc">ValidateInputDirectiveType</span> <span class="p">:</span> <span class="n">DirectiveType</span><span class="p">&lt;</span><span class="n">ValidateInputDirective</span><span class="p">&gt;</span> <span class="p">{</span> <span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Configure</span><span class="p">(</span><span class="n">IDirectiveTypeDescriptor</span><span class="p">&lt;</span><span class="n">ValidateInputDirective</span><span class="p">&gt;</span> <span class="n">descriptor</span><span class="p">)</span> <span class="p">{</span> <span class="k">base</span><span class="p">.</span><span class="nf">Configure</span><span class="p">(</span><span class="n">descriptor</span><span class="p">);</span> <span class="n">descriptor</span> <span class="p">.</span><span class="nf">Name</span><span class="p">(</span><span class="s">"validateInput"</span><span class="p">)</span> <span class="p">.</span><span class="nf">Location</span><span class="p">(</span><span class="n">DirectiveLocation</span><span class="p">.</span><span class="n">FieldDefinition</span><span class="p">)</span> <span class="p">.</span><span class="n">Use</span><span class="p">&lt;</span><span class="n">ValidateInputMiddleware</span><span class="p">&gt;();</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>With this defined we can now apply the directive in our schema.</p> <div class="language-graphql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span><span class="w"> </span><span class="n">Mutation</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">createPerson</span><span class="p">(</span><span class="n">input</span><span class="p">:</span><span class="w"> </span><span class="n">CreatePersonInput</span><span class="p">!)</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">Person</span><span class="w"> </span><span class="err">@</span><span class="n">validateInput</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <h2 id="validate-input-middleware">Validate Input Middleware</h2> <p>So now we have three approaches to apply your middleware depending on the how specific or broad you want to apply it, what does the middleware itself look like? In fact it’s pretty simple.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">ValidateInputMiddleware</span> <span class="p">{</span> <span class="k">private</span> <span class="k">readonly</span> <span class="n">FieldDelegate</span> <span class="n">_next</span><span class="p">;</span> <span class="k">public</span> <span class="nf">ValidateInputMiddleware</span><span class="p">(</span><span class="n">FieldDelegate</span> <span class="n">next</span><span class="p">)</span> <span class="p">{</span> <span class="n">_next</span> <span class="p">=</span> <span class="n">next</span><span class="p">;</span> <span class="p">}</span> <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">Invoke</span><span class="p">(</span><span class="n">IMiddlewareContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">context</span><span class="p">.</span><span class="n">FieldSelection</span><span class="p">.</span><span class="n">Arguments</span><span class="p">.</span><span class="n">Count</span> <span class="p">==</span> <span class="m">0</span><span class="p">)</span> <span class="p">{</span> <span class="k">await</span> <span class="nf">_next</span><span class="p">(</span><span class="n">context</span><span class="p">);</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span> <span class="kt">var</span> <span class="n">errors</span> <span class="p">=</span> <span class="n">context</span><span class="p">.</span><span class="n">FieldSelection</span><span class="p">.</span><span class="n">Arguments</span> <span class="p">.</span><span class="nf">Select</span><span class="p">(</span><span class="n">a</span> <span class="p">=&gt;</span> <span class="n">context</span><span class="p">.</span><span class="n">Argument</span><span class="p">&lt;</span><span class="kt">object</span><span class="p">&gt;(</span><span class="n">a</span><span class="p">.</span><span class="n">Name</span><span class="p">.</span><span class="n">Value</span><span class="p">))</span> <span class="p">.</span><span class="nf">SelectMany</span><span class="p">(</span><span class="n">ValidateObject</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">errors</span><span class="p">.</span><span class="nf">Any</span><span class="p">())</span> <span class="p">{</span> <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">error</span> <span class="k">in</span> <span class="n">errors</span><span class="p">)</span> <span class="p">{</span> <span class="n">context</span><span class="p">.</span><span class="nf">ReportError</span><span class="p">(</span><span class="n">ErrorBuilder</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span> <span class="p">.</span><span class="nf">SetCode</span><span class="p">(</span><span class="s">"error.validation"</span><span class="p">)</span> <span class="p">.</span><span class="nf">SetMessage</span><span class="p">(</span><span class="n">error</span><span class="p">.</span><span class="n">ErrorMessage</span><span class="p">)</span> <span class="p">.</span><span class="nf">SetExtension</span><span class="p">(</span><span class="s">"memberNames"</span><span class="p">,</span> <span class="n">error</span><span class="p">.</span><span class="n">MemberNames</span><span class="p">)</span> <span class="p">.</span><span class="nf">AddLocation</span><span class="p">(</span><span class="n">context</span><span class="p">.</span><span class="n">FieldSelection</span><span class="p">.</span><span class="n">Location</span><span class="p">.</span><span class="n">Line</span><span class="p">,</span> <span class="n">context</span><span class="p">.</span><span class="n">FieldSelection</span><span class="p">.</span><span class="n">Location</span><span class="p">.</span><span class="n">Column</span><span class="p">)</span> <span class="p">.</span><span class="nf">SetPath</span><span class="p">(</span><span class="n">context</span><span class="p">.</span><span class="n">Path</span><span class="p">)</span> <span class="p">.</span><span class="nf">Build</span><span class="p">());</span> <span class="p">}</span> <span class="n">context</span><span class="p">.</span><span class="n">Result</span> <span class="p">=</span> <span class="k">null</span><span class="p">;</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="k">await</span> <span class="nf">_next</span><span class="p">(</span><span class="n">context</span><span class="p">);</span> <span class="p">}</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">ValidationResult</span><span class="p">&gt;</span> <span class="nf">ValidateObject</span><span class="p">(</span><span class="kt">object</span> <span class="n">argument</span><span class="p">)</span> <span class="p">{</span> <span class="kt">var</span> <span class="n">results</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">ValidationResult</span><span class="p">&gt;();</span> <span class="n">Validator</span><span class="p">.</span><span class="nf">TryValidateObject</span><span class="p">(</span><span class="n">argument</span><span class="p">,</span> <span class="k">new</span> <span class="nf">ValidationContext</span><span class="p">(</span><span class="n">argument</span><span class="p">),</span> <span class="n">results</span><span class="p">,</span> <span class="n">validateAllProperties</span><span class="p">:</span> <span class="k">true</span><span class="p">);</span> <span class="k">return</span> <span class="n">results</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>This code is for a field level middleware (regardless of whether it’s applied at the schema for field level), if you want to use this as directive middleware simply change the type of the Invoke parmeter from <code class="language-plaintext highlighter-rouge">IMiddlewareContext</code> to <code class="language-plaintext highlighter-rouge">IDirectiveContext</code>.</p> <p>Walking through the code isn’t overly complicated, we get the values for all the arguments of the field and validate them combinng the results into a single list. If there are any errors then we report them as errors, set the result of the field to be null, otherwise we we call the next piece of middleware in the chain. This last step is important because it means that the mutation will never be called if there are validation errors.</p> <h2 id="conclusion">Conclusion</h2> <p>In this post we looking at middleware in the context of a GraphQL request and how we can use it to provide automatic validation of arguments. We can see how the middleware can determine to terminate the pipeline to ensure that under the correct circumstances the field is never resolved.</p> Mon, 08 Jul 2019 00:00:00 +0000 http://compiledexperience.com/blog/posts/graphql-validate-arguments http://compiledexperience.com/blog/posts/graphql-validate-arguments nigel.sampson@compiledexperience.com (Nigel Sampson) csharp graphql Exposing Validation Metadata in GraphQL <p>When we do input validation in our applications we want them on both the client and server for a couple of key reasons:</p> <ol> <li>Validation on the client introduces better opporutunities for a richer user experience.</li> <li>We validate on the server as well because we can’t trust the client.</li> </ol> <p>One other goal we have is to only define our validation rules once, if we have to define our client and server validation in separate locations in our code we run the risk of one location not being updated and having a drift between client and server.</p> <h3 id="how-is-this-done-in-net">How is this done in .NET?</h3> <p>ASP.NET has the a <a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.modelbinding.modelmetadata?view=aspnetcore-2.2"><code class="language-plaintext highlighter-rouge">ModelMetadata</code></a> system that is highly extensible via a provider model and enables a way to view the validation rules. The out of the provider uses the <code class="language-plaintext highlighter-rouge">System.ComponentModel.DataAnnotations</code> attributes for validation definition giving us something like:</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">CreatePersonInput</span> <span class="p">{</span> <span class="p">[</span><span class="nf">StringLength</span><span class="p">(</span><span class="m">100</span><span class="p">)]</span> <span class="k">public</span> <span class="kt">string</span> <span class="n">FirstName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;}</span> <span class="p">[</span><span class="n">Required</span><span class="p">]</span> <span class="p">[</span><span class="nf">StringLength</span><span class="p">(</span><span class="m">100</span><span class="p">)]</span> <span class="k">public</span> <span class="kt">string</span> <span class="n">LastName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;}</span> <span class="p">}</span> </code></pre></div></div> <p>The out of the box Razor view system can then use the model metadata to write these validation rules into the html which can then be used the a front end validation framework such as <code class="language-plaintext highlighter-rouge">jquery-validation-unobtrusive</code>. You can read more about the whole system in <a href="https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-2.2">Model validation in ASP.NET Core MVC and Razor Pages</a>.</p> <h3 id="the-graphql-approach">The GraphQL approach</h3> <p>How can reproduce parts of this approach in a GraphQL based system? Our goal is to define validation rules on the input objects that are arguments to our queries and mutations and then have a way to expose those rules to a client consuming our API.</p> <p>The <a href="https://graphql.github.io/graphql-spec/June2018/">GraphQL specification</a> doesn’t have way to define validation rules, but it does have the concept of <a href="https://graphql.github.io/learn/queries/#directives">directives</a> which fill much the same space as attributes do in C#. They’re a mechanism to annotate a GraphQL schema with metadata that can be consumed by the server, client or tool such as a code generator.</p> <p>For the example above we may want to have something like the following in our schema:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>input CreatePersonInput { firstName: String @stringLength(maximumLength: 100) lastName: String! @required @stringLength(maximumLength: 100) } </code></pre></div></div> <blockquote> <p>I’ve wrapped the directives onto the lines after the field, this is just for readaiblity, they can all be on the same line (space separated).</p> </blockquote> <blockquote> <p>It’s also arguable that the fact that <code class="language-plaintext highlighter-rouge">lastName</code> is defined as not being nullable and required is redundant, but I’ve included it here to match up with validation attributes.</p> </blockquote> <h3 id="defining-our-directives">Defining our directives</h3> <p>The first step is create our directives and register them with our schema. I’ve deliberately kept these simple but you can imagine extending them with custom validation messages and the like. We’re building on top of the <a href="https://hotchocolate.io/">Hot Chocolate</a> GraphQL library.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">RequiredDirective</span> <span class="p">{</span> <span class="p">}</span> <span class="k">public</span> <span class="k">class</span> <span class="nc">RequiredDirectiveType</span> <span class="p">:</span> <span class="n">DirectiveType</span><span class="p">&lt;</span><span class="n">RequiredDirective</span><span class="p">&gt;</span> <span class="p">{</span> <span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Configure</span><span class="p">(</span><span class="n">IDirectiveTypeDescriptor</span><span class="p">&lt;</span><span class="n">RequiredDirective</span><span class="p">&gt;</span> <span class="n">descriptor</span><span class="p">)</span> <span class="p">{</span> <span class="k">base</span><span class="p">.</span><span class="nf">Configure</span><span class="p">(</span><span class="n">descriptor</span><span class="p">);</span> <span class="n">descriptor</span><span class="p">.</span><span class="nf">Name</span><span class="p">(</span><span class="s">"required"</span><span class="p">);</span> <span class="n">descriptor</span><span class="p">.</span><span class="nf">Location</span><span class="p">(</span><span class="n">DirectiveLocation</span><span class="p">.</span><span class="n">InputFieldDefinition</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">public</span> <span class="k">class</span> <span class="nc">StringLengthDirective</span> <span class="p">{</span> <span class="k">public</span> <span class="kt">int</span> <span class="n">MaximumLength</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="k">public</span> <span class="k">class</span> <span class="nc">StringLengthDirectiveType</span> <span class="p">:</span> <span class="n">DirectiveType</span><span class="p">&lt;</span><span class="n">StringLengthDirective</span><span class="p">&gt;</span> <span class="p">{</span> <span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Configure</span><span class="p">(</span><span class="n">IDirectiveTypeDescriptor</span><span class="p">&lt;</span><span class="n">StringLengthDirective</span><span class="p">&gt;</span> <span class="n">descriptor</span><span class="p">)</span> <span class="p">{</span> <span class="k">base</span><span class="p">.</span><span class="nf">Configure</span><span class="p">(</span><span class="n">descriptor</span><span class="p">);</span> <span class="n">descriptor</span><span class="p">.</span><span class="nf">Name</span><span class="p">(</span><span class="s">"stringLength"</span><span class="p">);</span> <span class="n">descriptor</span><span class="p">.</span><span class="nf">Location</span><span class="p">(</span><span class="n">DirectiveLocation</span><span class="p">.</span><span class="n">InputFieldDefinition</span><span class="p">);</span> <span class="n">descriptor</span><span class="p">.</span><span class="nf">Argument</span><span class="p">(</span><span class="n">t</span> <span class="p">=&gt;</span> <span class="n">t</span><span class="p">.</span><span class="n">MaximumLength</span><span class="p">)</span> <span class="p">.</span><span class="nf">Name</span><span class="p">(</span><span class="s">"maximumLength"</span><span class="p">)</span> <span class="p">.</span><span class="n">Type</span><span class="p">&lt;</span><span class="n">NonNullType</span><span class="p">&lt;</span><span class="n">IntType</span><span class="p">&gt;&gt;();</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">schema</span> <span class="p">=</span> <span class="n">SchemaBuilder</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span> <span class="p">.</span><span class="n">RegisterDirective</span><span class="p">&lt;</span><span class="n">RequiredDirectiveType</span><span class="p">&gt;()</span> <span class="p">.</span><span class="n">RegisterDirective</span><span class="p">&lt;</span><span class="n">StringLengthDirectiveType</span><span class="p">&gt;()</span> <span class="p">.</span><span class="nf">Create</span><span class="p">();</span> </code></pre></div></div> <h3 id="code-first-schema">Code first schema</h3> <p>In a code first schema approach we would expect to see something like the following for our <code class="language-plaintext highlighter-rouge">CreatePersonInput</code>, this is just enough configuration to override the default nullable string for <code class="language-plaintext highlighter-rouge">LastName</code>.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">CreatePersonInputType</span> <span class="p">:</span> <span class="n">InputObjectType</span><span class="p">&lt;</span><span class="n">CreatePersonInput</span><span class="p">&gt;</span> <span class="p">{</span> <span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Configure</span><span class="p">(</span><span class="n">IInputObjectTypeDescriptor</span><span class="p">&lt;</span><span class="n">CreateCommunityMemberInput</span><span class="p">&gt;</span> <span class="n">descriptor</span><span class="p">)</span> <span class="p">{</span> <span class="k">base</span><span class="p">.</span><span class="nf">Configure</span><span class="p">(</span><span class="n">descriptor</span><span class="p">);</span> <span class="n">descriptor</span><span class="p">.</span><span class="nf">Field</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span> <span class="n">c</span><span class="p">.</span><span class="n">LastName</span><span class="p">)</span> <span class="p">.</span><span class="n">Type</span><span class="p">&lt;</span><span class="n">NonNullType</span><span class="p">&lt;</span><span class="n">StringType</span><span class="p">&gt;&gt;();</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>We could then get the schema outcome by adding the appropriate directives here</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">descriptor</span><span class="p">.</span><span class="nf">Field</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span> <span class="n">c</span><span class="p">.</span><span class="n">FirstName</span><span class="p">)</span> <span class="p">.</span><span class="nf">Directive</span><span class="p">(</span><span class="k">new</span> <span class="n">StringLengthDirective</span> <span class="p">{</span> <span class="n">MaximumLength</span> <span class="p">=</span> <span class="m">100</span> <span class="p">});</span> <span class="n">descriptor</span><span class="p">.</span><span class="nf">Field</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span> <span class="n">c</span><span class="p">.</span><span class="n">LastName</span><span class="p">)</span> <span class="p">.</span><span class="n">Type</span><span class="p">&lt;</span><span class="n">NonNullType</span><span class="p">&lt;</span><span class="n">StringType</span><span class="p">&gt;&gt;()</span> <span class="p">.</span><span class="nf">Directive</span><span class="p">(</span><span class="k">new</span> <span class="nf">RequiredDirective</span><span class="p">())</span> <span class="p">.</span><span class="nf">Directive</span><span class="p">(</span><span class="k">new</span> <span class="n">StringLengthDirective</span> <span class="p">{</span> <span class="n">MaximumLength</span> <span class="p">=</span> <span class="m">100</span> <span class="p">});</span> </code></pre></div></div> <p>This gives us the correct schema, but we’ve missed on our goal of only definining our validation rules once.</p> <h3 id="using-modelmetadata">Using ModelMetadata</h3> <p>The next step is using the model metadata provided to us to automatically discover which validation directives to apply to our schema. Here it’s important to understand a bit about how Hot Chocolate creates the schema for out types. First it will call <code class="language-plaintext highlighter-rouge">Configure</code> above and then infer fields based off properties on the type (this is why we didn’t need define <code class="language-plaintext highlighter-rouge">FistName</code> in the first example above). This means we need a hook point after the inference of the fields to then go through all of the fields and add our directives. This is called <a href="https://hotchocolate.io/docs/extending-types">type extension</a> in Hot Chocolate and the hook we’re looking for is <code class="language-plaintext highlighter-rouge">OnBeforeCreate</code>.</p> <p>First we’ll create an extension method onto the type descriptor that makes use of the type extension system.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">static</span> <span class="n">IInputObjectTypeDescriptor</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;</span> <span class="n">AddValidationDirectives</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="k">this</span> <span class="n">IInputObjectTypeDescriptor</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;</span> <span class="n">descriptor</span><span class="p">,</span> <span class="n">IModelMetadataProvider</span> <span class="n">metadataProvider</span><span class="p">)</span> <span class="p">{</span> <span class="n">descriptor</span> <span class="p">.</span><span class="nf">Extend</span><span class="p">()</span> <span class="p">.</span><span class="nf">OnBeforeCreate</span><span class="p">(</span><span class="n">d</span> <span class="p">=&gt;</span> <span class="nf">AddValidationDirectives</span><span class="p">(</span><span class="n">d</span><span class="p">,</span> <span class="n">metadataProvider</span><span class="p">));</span> <span class="k">return</span> <span class="n">descriptor</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <p>and then the meat of the whole approach, we use the metadata provider to find all properties that have validators. We then locate the matching field based on name and attach directives based on which validation attributes are provided.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="k">void</span> <span class="nf">AddValidationDirectives</span><span class="p">(</span><span class="n">InputObjectTypeDefinition</span> <span class="n">definition</span><span class="p">,</span> <span class="n">IModelMetadataProvider</span> <span class="n">metadataProvider</span><span class="p">)</span> <span class="p">{</span> <span class="kt">var</span> <span class="n">metadata</span> <span class="p">=</span> <span class="n">metadataProvider</span><span class="p">.</span><span class="nf">GetMetadataForType</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">CreateCommunityMemberInput</span><span class="p">));</span> <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">propertyMetadata</span> <span class="k">in</span> <span class="n">metadata</span><span class="p">.</span><span class="n">Properties</span><span class="p">.</span><span class="nf">Where</span><span class="p">(</span><span class="n">p</span> <span class="p">=&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">HasValidators</span> <span class="p">??</span> <span class="k">false</span><span class="p">))</span> <span class="p">{</span> <span class="kt">var</span> <span class="n">field</span> <span class="p">=</span> <span class="n">definition</span><span class="p">.</span><span class="n">Fields</span><span class="p">.</span><span class="nf">SingleOrDefault</span><span class="p">(</span><span class="n">f</span> <span class="p">=&gt;</span> <span class="n">f</span><span class="p">.</span><span class="n">Name</span><span class="p">.</span><span class="nf">Equals</span><span class="p">(</span><span class="n">propertyMetadata</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span> <span class="n">StringComparison</span><span class="p">.</span><span class="n">OrdinalIgnoreCase</span><span class="p">));</span> <span class="k">if</span> <span class="p">(</span><span class="n">field</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span> <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">validator</span> <span class="k">in</span> <span class="n">propertyMetadata</span><span class="p">.</span><span class="n">ValidatorMetadata</span><span class="p">)</span> <span class="p">{</span> <span class="k">switch</span> <span class="p">(</span><span class="n">validator</span><span class="p">)</span> <span class="p">{</span> <span class="k">case</span> <span class="n">RequiredAttribute</span> <span class="n">required</span><span class="p">:</span> <span class="n">field</span><span class="p">.</span><span class="n">Directives</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="nf">DirectiveDefinition</span><span class="p">(</span><span class="k">new</span> <span class="nf">RequiredDirective</span><span class="p">()));</span> <span class="k">break</span><span class="p">;</span> <span class="k">case</span> <span class="n">StringLengthAttribute</span> <span class="n">stringLength</span><span class="p">:</span> <span class="n">field</span><span class="p">.</span><span class="n">Directives</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="nf">DirectiveDefinition</span><span class="p">(</span><span class="k">new</span> <span class="n">StringLengthDirective</span> <span class="p">{</span> <span class="n">MaximumLength</span> <span class="p">=</span> <span class="n">stringLength</span><span class="p">.</span><span class="n">MaximumLength</span> <span class="p">}));</span> <span class="k">break</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>Our schema code then simply becomes</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">CreatePersonInputType</span> <span class="p">:</span> <span class="n">InputObjectType</span><span class="p">&lt;</span><span class="n">CreatePersonInput</span><span class="p">&gt;</span> <span class="p">{</span> <span class="k">readonly</span> <span class="n">IModelMetadataProvider</span> <span class="n">_metadataProvider</span><span class="p">;</span> <span class="k">public</span> <span class="nf">CreateCommunityMemberInputType</span><span class="p">(</span><span class="n">IModelMetadataProvider</span> <span class="n">metadataProvider</span><span class="p">)</span> <span class="p">{</span> <span class="n">_metadataProvider</span> <span class="p">=</span> <span class="n">metadataProvider</span><span class="p">;</span> <span class="p">}</span> <span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Configure</span><span class="p">(</span><span class="n">IInputObjectTypeDescriptor</span><span class="p">&lt;</span><span class="n">CreateCommunityMemberInput</span><span class="p">&gt;</span> <span class="n">descriptor</span><span class="p">)</span> <span class="p">{</span> <span class="k">base</span><span class="p">.</span><span class="nf">Configure</span><span class="p">(</span><span class="n">descriptor</span><span class="p">);</span> <span class="n">descriptor</span><span class="p">.</span><span class="nf">Field</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span> <span class="n">c</span><span class="p">.</span><span class="n">LastName</span><span class="p">)</span> <span class="p">.</span><span class="n">Type</span><span class="p">&lt;</span><span class="n">NonNullType</span><span class="p">&lt;</span><span class="n">StringType</span><span class="p">&gt;&gt;();</span> <span class="n">descriptor</span><span class="p">.</span><span class="nf">AddValidationDirectives</span><span class="p">(</span><span class="n">_metadataProvider</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <h3 id="conclusion">Conclusion</h3> <p>We now have in place an extensible approach for defining our validation once per model and then exposing via the GraphQL schema to consuming clients.</p> <p>Next time we’ll look at automatically validating those arguments on the server.</p> Fri, 21 Jun 2019 00:00:00 +0000 http://compiledexperience.com/blog/posts/grapql-validation-metadata http://compiledexperience.com/blog/posts/grapql-validation-metadata nigel.sampson@compiledexperience.com (Nigel Sampson) csharp graphql A better approach to GraphQL renames <p>As it typically happens I write a post on how I do something, it gets some decent traffic and then I find a better way to do it.</p> <p>In this case I’m referring to a post from a few weeks ago about <a href="/blog/posts/stitched-graphql-rename">Handling name collisions in GraphQL schema stitching</a>, in that post I highlighted a way to use <code class="language-plaintext highlighter-rouge">AddMergedDocumentRewriter</code> to rewrite the merged schema document. It turns out there’s a better way…</p> <p><a href="https://hotchocolate.io/">Hot Chocolate</a> supports an <code class="language-plaintext highlighter-rouge">ITypeRewriter</code> interface that when registered will be called for each type in the merged schema. Our <code class="language-plaintext highlighter-rouge">RenameTypeRewriter</code> looks like the following which is a lot cleaner in my opinion.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">RenameTypeRewriter</span> <span class="p">:</span> <span class="n">ITypeRewriter</span> <span class="p">{</span> <span class="k">public</span> <span class="n">ITypeDefinitionNode</span> <span class="nf">Rewrite</span><span class="p">(</span><span class="n">ISchemaInfo</span> <span class="n">schema</span><span class="p">,</span> <span class="n">ITypeDefinitionNode</span> <span class="n">typeDefinition</span><span class="p">)</span> <span class="p">{</span> <span class="kt">var</span> <span class="n">renameDirective</span> <span class="p">=</span> <span class="n">typeDefinition</span><span class="p">.</span><span class="n">Directives</span><span class="p">.</span><span class="nf">SingleOrDefault</span><span class="p">(</span><span class="n">d</span> <span class="p">=&gt;</span> <span class="n">d</span><span class="p">.</span><span class="n">Name</span><span class="p">.</span><span class="n">Value</span> <span class="p">==</span> <span class="n">RenameDirectiveType</span><span class="p">.</span><span class="n">DirectiveName</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">renameDirective</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span> <span class="kt">var</span> <span class="n">newNameArgumment</span> <span class="p">=</span> <span class="n">renameDirective</span><span class="p">.</span><span class="n">Arguments</span><span class="p">.</span><span class="nf">Single</span><span class="p">(</span><span class="n">a</span> <span class="p">=&gt;</span> <span class="n">a</span><span class="p">.</span><span class="n">Name</span><span class="p">.</span><span class="n">Value</span> <span class="p">==</span> <span class="n">RenameDirectiveType</span><span class="p">.</span><span class="n">ArgumentName</span> <span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">newNameArgumment</span><span class="p">.</span><span class="n">Value</span> <span class="k">is</span> <span class="n">StringValueNode</span> <span class="n">stringValue</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">typeDefinition</span><span class="p">.</span><span class="nf">Rename</span><span class="p">(</span><span class="n">stringValue</span><span class="p">.</span><span class="n">Value</span><span class="p">,</span> <span class="n">schema</span><span class="p">.</span><span class="n">Name</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">return</span> <span class="n">typeDefinition</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>Instead of our call to <code class="language-plaintext highlighter-rouge">AddMergedDocumentRewriter</code> we now have</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">.</span><span class="nf">AddTypeRewriter</span><span class="p">(</span><span class="k">new</span> <span class="nf">RenameTypeRewriter</span><span class="p">())</span> </code></pre></div></div> Tue, 04 Jun 2019 00:00:00 +0000 http://compiledexperience.com/blog/posts/better-rename http://compiledexperience.com/blog/posts/better-rename nigel.sampson@compiledexperience.com (Nigel Sampson) csharp graphql