Compiled Experience Windows Platform Development http://compiledexperience.com en Tue, 21 Sep 2021 21:53:52 +0000 Tue, 21 Sep 2021 21:53:52 +0000 Adding cross cutting concerns to a GraphQL service <h2 id="what-are-cross-cutting-concerns">What are cross cutting concerns?</h2> <p>Typically within a system we can consider something a cross cutting concern if it has to be involved with every action (in this case a request), these things tend to be security, logging, validation and more.</p> <h2 id="using-middleware">Using middleware</h2> <p>The idea of a pipeline of middleware that serves every request makes the process of adding some cross cutting concerns to our GraphQL service really quite easy. The <a href="https://chillicream.com/">Hot Chocolate</a> framework allows you to add “request middleware” that runs for every request, or “field middleware” that runs on every field within that that request. We may use the “request middleware” to ensure basis metrics such as operation name and timings are sent to our observablity system to allow for easier diagbosis of problems. We may use “field middleware” to ensure every argument on a field is correctly validated”. Both of these are done at the service setup.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">services</span><span class="p">.</span><span class="nf">AddGraphQLServer</span><span class="p">()</span> <span class="p">.</span><span class="n">UseRequest</span><span class="p">&lt;</span><span class="n">LoggingMiddleware</span><span class="p">&gt;()</span> <span class="c1">// Adds middleware to every request</span> <span class="p">.</span><span class="n">UseField</span><span class="p">&lt;</span><span class="n">ValidationMIddleware</span><span class="p">&gt;()</span> <span class="c1">// Adds middleware to every field in the request</span> </code></pre></div></div> <p>One problem with field middleware is that it’s a pretty big hammer, we’re essentially adding a little bit of code to the resolution of every single field in our schema. For some concerns this may be fine, the scenario we wish to cover is required on 90% of the fields in our schema. However sometimes this may feel unwarranted, we want to add field middleware to a smaller subset of our fields and not go with a blanket approach.</p> <p>We can target specific fields a few different ways depending on how we’re setting up our schema, if we’re using a “code first approch” it would look something like the following, here we’re being very targeted, applying the middleware to only the fielsd we care about.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">void</span> <span class="nf">Configure</span><span class="p">(</span><span class="n">IObjectTypeDescriptor</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="s">"productSearch"</span><span class="p">)</span> <span class="p">.</span><span class="n">Use</span><span class="p">&lt;</span><span class="n">ValidationMIddleware</span><span class="p">&gt;()</span> <span class="p">}</span> <span class="c1">// reset of field definition here</span> </code></pre></div></div> <p>If you’re using a schema first approach it’s a bit more complicated, typically the approach I’ve used before is to declare a directive, attach the middleware to the directive and then apply the directive on the schema where I want the middleware. In fact if you’ve used the <code class="language-plaintext highlighter-rouge">@authorize</code> directive from <a href="https://chillicream.com/">Hot Chocolate</a>, this is exactly what is happening, we’re using the directive to apply a piece of middleware to the field that will enforce security.</p> <p>There are some risks with this approach though, we’re relying on the developer to remember every place we should be putting the middleware as they add new types and fields to our schema, there is a reasonable chance that the middleware would be forgotten. For a cross cutting concern such as security we could have an unsecured field or other incidents.</p> <h2 id="creating-a-pit-of-success">Creating a pit of success</h2> <p>As a senior developer I really like thinking about problems and how to make them easier for others on the team. The phrase “pit of success” is a way to describe this, we want to make the easy, secure, performant approach the easiest to implement, if the easiest path is the best path then we’ll “fall” into the correct approach. For our current problem, is there way we can specifically target our middleware without applying it everywhere but not rely on a developer remembering to add it. We can do this with a <code class="language-plaintext highlighter-rouge">TypeInterceptor</code>, this is a class that we use at schema creation to inject cross cutting concerns in an automatic manner.</p> <p>The class allows us to inject behavior at different times, for this scenario we want to do it just before the type is completed. We first check to see if we’re working with a object type, from there we check each field, if it as any arguments we add our validation middleware.</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">ValidationTypeInterceptor</span> <span class="p">:</span> <span class="n">TypeInterceptor</span> <span class="p">{</span> <span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnBeforeCompleteType</span><span class="p">(</span><span class="n">ITypeCompletionContext</span> <span class="n">completionContext</span><span class="p">,</span> <span class="n">DefinitionBase</span><span class="p">?</span> <span class="n">definition</span><span class="p">,</span> <span class="n">IDictionary</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">object</span><span class="p">?&gt;</span> <span class="n">contextData</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">definition</span> <span class="k">is</span> <span class="n">ObjectTypeDefinition</span> <span class="n">objectDefinition</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">fieldDefinition</span> <span class="k">in</span> <span class="n">objectDefinition</span><span class="p">.</span><span class="n">Fields</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">fieldDefinition</span><span class="p">.</span><span class="n">Arguments</span><span class="p">.</span><span class="n">Count</span> <span class="p">&gt;</span> <span class="m">0</span><span class="p">)</span> <span class="p">{</span> <span class="n">fieldDefinition</span><span class="p">.</span><span class="n">MiddlewareComponents</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">FieldClassMiddlewareFactory</span><span class="p">.</span><span class="n">Create</span><span class="p">&lt;</span><span class="n">ValidationMiddleware</span><span class="p">&gt;());</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>Using our new type interceptor is as easy as the following.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">services</span><span class="p">.</span><span class="nf">AddGraphQLServer</span><span class="p">()</span> <span class="p">.</span><span class="n">TryAddTypeInterceptor</span><span class="p">&lt;</span><span class="n">ValidationTypeInterceptor</span><span class="p">&gt;()</span> <span class="p">...</span> </code></pre></div></div> <p>We’ve now created our “pit of success”, we no longer need to rely on developers to remember to apply the correct middleware at the correct times, we can by convention apply it to the correct places.</p> Wed, 22 Sep 2021 00:00:00 +0000 http://compiledexperience.com/blog/posts/crosscutting-grapql http://compiledexperience.com/blog/posts/crosscutting-grapql [email protected] (Nigel Sampson) Why doesn't GraphQL "SELECT *" <p>Over the last year or so I’ve helped onboard a number of engineers at Pushpay to how we do GraphQL. One question that often comes up is</p> <blockquote> <p>How can I select all fields on a type?</p> </blockquote> <p>GraphQL doesn’t have the equivalent of <code class="language-plaintext highlighter-rouge">SELECT * FROM Object</code>, you can only get the fields in the response that are in the query that was sent and in my opinion this is a very good thing. So why is that?</p> <p>The major reason for me is the ability to make safe breaking changes to a schema. Occasionally (ideally as little as possible) we’ll need to make a breaking change to a schema, the way we tend to approach these schema changes is a pair of widening and narrowing changes. To use an exmaple we want to change the type of a field, for instance let’s assume we had a type <code class="language-plaintext highlighter-rouge">Payment</code> with had an <code class="language-plaintext highlighter-rouge">amount</code> field of type <code class="language-plaintext highlighter-rouge">Decimal</code>, however now we want to include the curreny as well and it makes sense to tie into the amount. A typical approach would look like:</p> <ol> <li>Add new field <code class="language-plaintext highlighter-rouge">amountPaid</code> to <code class="language-plaintext highlighter-rouge">Payment</code> with an object type that includes <code class="language-plaintext highlighter-rouge">amount</code> and <code class="language-plaintext highlighter-rouge">currency</code>. It’s worth nothing that we can’t share field names so naming the new field can often be problematic, you may end up with a temporary name and run a second migration to rename the field back to the original. At the same time we can mark the <code class="language-plaintext highlighter-rouge">amount</code> field with <code class="language-plaintext highlighter-rouge">@deprecated(reason: "Use amountPaid")</code>.</li> </ol> <p>Now that we have marked the field as deprecated in the schema we can add some functionality to our backend. We’ve added a <code class="language-plaintext highlighter-rouge">DiagnosticEventListener</code> that logs any usage of a deprecated field, the include things like API client ids, user agents etc to help track down who is still selecting the deprecated field. This lets use target communication better about how to inform around these changes and monitor ongoing usage of that field.</p> <ol> <li>Once we’re no longer seeing log messages around usage of the field we can safely remove the field from the schema knowing we won’t break functionality of a client.</li> </ol> <p>Now if we had <code class="language-plaintext highlighter-rouge">SELECT *</code> and our clients were being lazy and decided to use this we wouldn’t be able to tell if a client actually cared about the <code class="language-plaintext highlighter-rouge">amount</code> field, or was just selecting all fields. This would mean we couldn’t communicate effectively with our clients nor determine when it was safe to make the breaking change.</p> <p>All in all a good design decision.</p> Fri, 20 Aug 2021 00:00:00 +0000 http://compiledexperience.com/blog/posts/gql-select-all http://compiledexperience.com/blog/posts/gql-select-all [email protected] (Nigel Sampson) GraphQL Observability <p>Whenever we have a web application we’ll typically want to be able to observe it in production. Typically common questions will be:</p> <ul> <li>Which endpoints are being exercised the most?</li> <li>Which endpoints are running slower than normal?</li> <li>Which endpoints are returning errors to our users?</li> </ul> <p>Eagle eyed readers will notice all of these quetsions are about endpoints or routes. Most Application Performance Monotiring (APM) products will have a built in knowledge of HTTP and easily be able to show this to you. <a href="https://newrelic.com/">New Relic</a> which I’ll use for the rest of my examples even has an understanding of MVC built in and will classify transactions (requests) using their controller and action names rather than just the url.</p> <h3 id="what-about-graphql">What about GraphQL?</h3> <p>The way a GraphQL server works is that all queries and mutations are POST’s to the same url (typically something like <code class="language-plaintext highlighter-rouge">/graphql</code>) which means you get something like the following in New Relic.</p> <p><img src="/content/images/posts/new-relic-old.png" alt="New Relic without operations" /></p> <p>Now this data won’t let you answer any of those three questions, and in fact GraphQL comes with even more nuance if you’re running a public GraphQL API which we can discuss another time.</p> <h3 id="adding-observability">Adding Observability</h3> <p>The <a href="https://chillicream.com/">Hot Chocolate</a> has the concept of a <code class="language-plaintext highlighter-rouge">DiagnosticEventListener</code>, what we’ll do is create our own <code class="language-plaintext highlighter-rouge">NewRelicDiagnosticEventListener</code> to append some extra information to the current transaction to make it more useful.</p> <p>The <code class="language-plaintext highlighter-rouge">DiagnosticEventListener</code> class has a number of methods you can override depending on what you care about in terms of your diagnostics. For this exacmple we care about <code class="language-plaintext highlighter-rouge">ExecuteRequest</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">NewRelicDiagnosticEventListener</span> <span class="p">:</span> <span class="n">DiagnosticEventListener</span> <span class="p">{</span> <span class="k">public</span> <span class="k">override</span> <span class="n">IActivityScope</span> <span class="nf">ExecuteRequest</span><span class="p">(</span><span class="n">IRequestContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// implementation goes here</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>Now the API pattern for the <code class="language-plaintext highlighter-rouge">DiagnosticEventListener</code> is an intersting one that I’d never come across before and it’s worth a bit of explanation. Typeically for these diagnostic API’s we’ll need a hook for the start of a request as well one at the end as sometimes you’ll want to set up things at the start of the requuest and then collect / use them at the end. This can make a hefty API surface if for every “event” there would need to be two hooks. The <a href="https://chillicream.com/">Hot Chocolate</a> developers resolved this by introducing <code class="language-plaintext highlighter-rouge">IActivityScope</code> which is simply a rename of <code class="language-plaintext highlighter-rouge">IDisposable</code>.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">interface</span> <span class="nc">IActivityScope</span> <span class="p">:</span> <span class="n">IDisposable</span> <span class="p">{</span> <span class="p">}</span> </code></pre></div></div> <p>The idea is the framework will call <code class="language-plaintext highlighter-rouge">ExecuteRequest</code> at the start of the request. We’ll then request an <code class="language-plaintext highlighter-rouge">IActivityScope</code> that will be disposed by the framework at the end of the request.</p> <p>For our example we’ll want to do changes to the New Relic transaction at the end of the request because we’ll want to use information parsed from the request body.</p> <p>Here’s our final version, it returns a custom <code class="language-plaintext highlighter-rouge">RequestActvityScope</code> that on disposale will set the name of the current transaction in New Relic to be the incoming operation name.</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">NewRelicDiagnosticEventListener</span> <span class="p">:</span> <span class="n">DiagnosticEventListener</span> <span class="p">{</span> <span class="k">public</span> <span class="k">override</span> <span class="n">IActivityScope</span> <span class="nf">ExecuteRequest</span><span class="p">(</span><span class="n">IRequestContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="k">new</span> <span class="nf">RequestActvityScope</span><span class="p">(</span><span class="n">context</span><span class="p">);</span> <span class="p">}</span> <span class="k">private</span> <span class="k">class</span> <span class="nc">RequestActvityScope</span> <span class="p">:</span> <span class="n">IActivityScope</span> <span class="p">{</span> <span class="k">private</span> <span class="k">readonly</span> <span class="n">IRequestContext</span> <span class="n">context</span><span class="p">;</span> <span class="k">private</span> <span class="kt">bool</span> <span class="n">_isDisposed</span><span class="p">;</span> <span class="k">public</span> <span class="nf">RequestActvityScope</span><span class="p">(</span><span class="n">IRequestContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="n">context</span> <span class="p">=</span> <span class="n">context</span><span class="p">;</span> <span class="p">}</span> <span class="k">public</span> <span class="k">void</span> <span class="nf">Dispose</span><span class="p">()</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">_isDisposed</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span> <span class="n">NewRelic</span><span class="p">.</span><span class="n">Api</span><span class="p">.</span><span class="n">Agent</span><span class="p">.</span><span class="n">NewRelic</span><span class="p">.</span><span class="nf">SetTransactionName</span><span class="p">(</span><span class="s">"GraphQL"</span><span class="p">,</span> <span class="n">context</span><span class="p">.</span><span class="n">Operation</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="s">"unknown"</span><span class="p">);</span> <span class="n">_isDisposed</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>We can then use it with our setup code</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">services</span><span class="p">.</span><span class="nf">AddGraphQLServer</span><span class="p">()</span> <span class="p">.</span><span class="n">AddDiagnosticEventListener</span><span class="p">&lt;</span><span class="n">NewRelicDiagnosticEventListener</span><span class="p">&gt;()</span> <span class="p">.</span><span class="n">AddQueryType</span><span class="p">&lt;</span><span class="n">Query</span><span class="p">&gt;();</span> </code></pre></div></div> <p>Now we’ll see something more useful in New Relic, all our traffic previously grouped up in <code class="language-plaintext highlighter-rouge">/graphql</code> has been broken apart into the different operations our front end code is making which makes it a lot easier to answer the questions from the start of the post.</p> <p><img src="/content/images/posts/new-relic-new.png" alt="New Relic with operations" /></p> <h2 id="conclusion">Conclusion</h2> <p>Out of the box most APM tools will do a poor job of observability into GraphQL operations due to how HTTP is used. We need to do more work on our server to assist the APM to enable us as developers to support our GraphQL API’s in production.</p> Wed, 04 Aug 2021 00:00:00 +0000 http://compiledexperience.com/blog/posts/graphql-observability http://compiledexperience.com/blog/posts/graphql-observability [email protected] (Nigel Sampson) Implementing resource based authorization in GraphQL <p>Late last year I wrote about <a href="https://compiledexperience.com/blog/posts/securing-graphql">Securing a GraphQL endpoint</a>, using <a href="https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-5.0">ASP.NET Core policy based authorization</a>.</p> <p>The short version of this is that we can create authorization policies and use <a href="https://chillicream.com/">Hot Chocolate</a> to apply those policies to fields in our schema. If the current user cannot fufill those policy requirements then the execution of that field is stopped and <code class="language-plaintext highlighter-rouge">null</code> is returned. It’s impotant to note that this doesn’t stop the entire query but just the field in question.</p> <h2 id="what-is-resource-based-authorization">What is “resource based authorization”?</h2> <p>All of the examples used in the above articles we can think of “declarative authorization” where able to simply annotate the schema with our authorization requirements.</p> <p>Sometimes however we need to take into account the data (or resource) in question needs to be taken into account. Microsoft terms this <a href="https://docs.microsoft.com/en-us/aspnet/core/security/authorization/resourcebased?view=aspnetcore-5.0">resource based authorization</a> or “imperative authorization”.</p> <p>Examples of this include:</p> <ul> <li>Only the author of the document can edit it.</li> <li>When viewing customer details if they are a minor then a specific permission is required.</li> </ul> <p>You’ll notice that in that article the solution involves adding code to particular MVC actions to enforce thse policies.</p> <h2 id="an-implementation-in-graphql">An implementation in GraphQL</h2> <h3 id="authorization-in-the-resolver">Authorization in the resolver</h3> <p>We can follow a similar implementation model in GraphQL as shown in the above article in MVC. This would be how we add the policy evaluation is the resolver for the field.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">Document</span><span class="p">&gt;</span> <span class="nf">GetDocument</span><span class="p">(</span><span class="n">IResolverContext</span> <span class="n">context</span><span class="p">,</span> <span class="n">Guid</span> <span class="n">documentId</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// Hot Chocolate pushes the current user into `ContextData`</span> <span class="kt">var</span> <span class="n">user</span> <span class="p">=</span> <span class="p">(</span><span class="n">ClaimsPrincipal</span><span class="p">)</span> <span class="n">context</span><span class="p">.</span><span class="n">ContextData</span><span class="p">[</span><span class="k">nameof</span><span class="p">(</span><span class="n">ClaimsPrincipal</span><span class="p">)];</span> <span class="kt">var</span> <span class="n">document</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_repository</span><span class="p">.</span><span class="nf">Find</span><span class="p">(</span><span class="n">documentId</span><span class="p">);</span> <span class="kt">var</span> <span class="n">authResult</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_authorizationService</span><span class="p">.</span><span class="nf">AuthorizeAsync</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">document</span><span class="p">,</span> <span class="s">"EditPolicy"</span><span class="p">);</span> <span class="k">return</span> <span class="n">authResult</span><span class="p">.</span><span class="n">Succeeded</span> <span class="p">?</span> <span class="n">document</span> <span class="p">:</span> <span class="k">null</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <p>In this code we can reuse the requirement and requirement handlers in <a href="https://docs.microsoft.com/en-us/aspnet/core/security/authorization/resourcebased?view=aspnetcore-5.0">the article</a> which is quite nice.</p> <h3 id="creating-a-more-reusable-policy">Creating a more reusable policy</h3> <p>We can implement this in another way in <a href="https://chillicream.com/">Hot Chocolate 11</a> which I think puts in a better place, the change they’ve added is an option to the <code class="language-plaintext highlighter-rouge">@authorize</code> directive which controls whether the policy evaluation occurs before or after the resolver (the only and now default behavior was before the resolver). This coupled with the fact that Hot Chocolate injects the <code class="language-plaintext highlighter-rouge">IDirectiveContext</code> as the reource being evaluated.</p> <p>We can then modify the requirement handler as follows, here we look at the result of the resolver to get the document and run the same check as we did before.</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">DocumentAuthorizationHandler</span> <span class="p">:</span> <span class="n">AuthorizationHandler</span><span class="p">&lt;</span><span class="n">SameAuthorRequirement</span><span class="p">,</span> <span class="n">Document</span><span class="p">&gt;</span> <span class="p">{</span> <span class="k">protected</span> <span class="k">override</span> <span class="n">Task</span> <span class="nf">HandleRequirementAsync</span><span class="p">(</span><span class="n">AuthorizationHandlerContext</span> <span class="n">context</span><span class="p">,</span> <span class="n">SameAuthorRequirement</span> <span class="n">requirement</span><span class="p">,</span> <span class="n">IMiddlewareContext</span> <span class="n">middleware</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">middleware</span><span class="p">.</span><span class="n">Result</span> <span class="k">is</span> <span class="n">Document</span> <span class="n">document</span> <span class="p">&amp;&amp;</span> <span class="n">context</span><span class="p">.</span><span class="n">User</span><span class="p">.</span><span class="n">Identity</span><span class="p">?.</span><span class="n">Name</span> <span class="p">==</span> <span class="n">document</span><span class="p">.</span><span class="n">Author</span><span class="p">)</span> <span class="p">{</span> <span class="n">context</span><span class="p">.</span><span class="nf">Succeed</span><span class="p">(</span><span class="n">requirement</span><span class="p">);</span> <span class="p">}</span> <span class="k">return</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>We can now apply our “imperative policy” is a declarative fashion as follows.</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">document</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">Document</span><span class="w"> </span><span class="err">@</span><span class="n">authorize</span><span class="p">(</span><span class="n">policy</span><span class="p">:</span><span class="w"> </span><span class="err">"</span><span class="n">EditPolicy</span><span class="err">"</span><span class="p">,</span><span class="w"> </span><span class="n">apply</span><span class="p">:</span><span class="w"> </span><span class="n">AFTER_RESOLVER</span><span class="p">)</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>One of the major benefits of this approach is I can then easily apply this policy on multiple fields in our schema rather than having to go through our entire code base and inject the code. Even more useful is the ability to apply the directive to types which applies the policy to all fields on the type.</p> <h2 id="conclusion">Conclusion</h2> <p>With the changes in Hot Chocolate 11 controlling when authorization directives are applied can let use build policies that implement “resource based authorization” in way that can be used across the entire schema.</p> Fri, 26 Feb 2021 00:00:00 +0000 http://compiledexperience.com/blog/posts/gql-resouce-auth http://compiledexperience.com/blog/posts/gql-resouce-auth [email protected] (Nigel Sampson) Implementing GraphQL Relay Node support in Hot Chocolate <h2 id="what-is-relay">What is Relay?</h2> <p>The <a href="https://spec.graphql.org/">GraphQL spec</a> isn’t very prescriptive on the structure of your schema leaving the design completely in your hands. This means we’re likely to see quite a few different approaches to certain patterns between different services. One specification that has become pretty common across services is <a href="https://relay.dev/en/">Relay</a>.</p> <p>If you check out their webpage you’ll notice that it advertises itself with</p> <blockquote> <p>Relay is a JavaScript framework for building data-driven React applications powered by GraphQL, designed from the ground up to be easy to use, extensible and, most of all, performant. Relay accomplishes this with static queries and ahead-of-time code generation.</p> </blockquote> <p>In order to achieve this <a href="https://relay.dev/en/">Relay</a> has to make some assumptions about the structure of your GraphQL service, these are documented at <a href="https://relay.dev/docs/en/graphql-server-specification.html">GraphQL Server Specification</a>. We won’t described it in detail here but ultimately it covers three areas.</p> <ol> <li>Being able to fetch arbitrary objects by id.</li> <li>Paging one to many relationships between objects.</li> <li>Mutation inputs and paylods.</li> </ol> <p>For this post we’ll cover implementing part of this specification using <a href="https://chillicream.com/">Hot Chocolate</a>, in particular the first requirement around arbitrary objects by id.</p> <p>The <a href="https://relay.dev/docs/en/graphql-server-specification.html">server specific</a> defines an interface <code class="language-plaintext highlighter-rouge">Node</code> to designate service objects and being able to accessed globally by id.</p> <div class="language-graphql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">interface</span><span class="w"> </span><span class="n">Node</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="p">}</span><span class="w"> </span></code></pre></div></div> <p>Objects implementing this interface may look something like</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">Product</span><span class="w"> </span><span class="k">implements</span><span class="w"> </span><span class="n">Node</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="n">category</span><span class="p">:</span><span class="w"> </span><span class="n">ProductCategory</span><span class="p">!</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">Order</span><span class="w"> </span><span class="k">implements</span><span class="w"> </span><span class="n">Node</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">customer</span><span class="p">:</span><span class="w"> </span><span class="n">Customer</span><span class="p">!</span><span class="w"> </span><span class="n">createdOn</span><span class="p">:</span><span class="w"> </span><span class="n">Instant</span><span class="p">!</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>The specification also defines a top level query field that takes an <code class="language-plaintext highlighter-rouge">ID</code> and returns a <code class="language-plaintext highlighter-rouge">Node</code>.</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">node</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">Node</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>This would allow to construct queries that look like</p> <div class="language-graphql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">query</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">node</span><span class="p">(</span><span class="n">id</span><span class="p">:</span><span class="w"> </span><span class="s2">"Tm90aWZpY2F0aW9uCmQxNTox"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="k">on</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">name</span><span class="w"> </span><span class="n">category</span><span class="w"> </span><span class="p">}</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>It should be obvious from this that the id values for each our nodes should be globally unique.</p> <p>So what’s the value here? This patterns means we may not need to build top level fields to query different objects by id but all can go through this <code class="language-plaintext highlighter-rouge">node</code> field. In provides standards around identifiers as well.</p> <h2 id="implementing-in-hot-chocolate">Implementing in Hot Chocolate</h2> <p>Our first step will be enabling Relay functionality on our server, this is done during our server setup.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">services</span> <span class="p">.</span><span class="nf">AddGraphQLServer</span><span class="p">()</span> <span class="p">.</span><span class="nf">EnableRelaySupport</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> </code></pre></div></div> <p>Once this is done we can no add <code class="language-plaintext highlighter-rouge">Node</code> support to any of our types. Let’s assume we have a <code class="language-plaintext highlighter-rouge">ProductType</code> class describing <code class="language-plaintext highlighter-rouge">Product</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">ProductType</span> <span class="p">:</span> <span class="n">ObjectType</span><span class="p">&lt;</span><span class="n">Product</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">Product</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">ImplementsNode</span><span class="p">()</span> <span class="p">.</span><span class="nf">IdField</span><span class="p">(</span><span class="n">n</span> <span class="p">=&gt;</span> <span class="n">n</span><span class="p">.</span><span class="n">Id</span><span class="p">)</span> <span class="p">.</span><span class="nf">ResolveNode</span><span class="p">(</span><span class="k">async</span> <span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">id</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="kt">var</span> <span class="n">productRepository</span> <span class="p">=</span> <span class="n">context</span><span class="p">.</span><span class="n">Service</span><span class="p">&lt;</span><span class="n">IProductRepository</span><span class="p">&gt;();</span> <span class="k">return</span> <span class="k">await</span> <span class="n">productRepository</span><span class="p">.</span><span class="nf">GetById</span><span class="p">(</span><span class="n">id</span><span class="p">);</span> <span class="p">});</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>Here we’re doing three things.</p> <ol> <li>Saying that <code class="language-plaintext highlighter-rouge">Product</code> will implement the <code class="language-plaintext highlighter-rouge">Node</code> interface.</li> <li>Describe which property on the <code class="language-plaintext highlighter-rouge">Product</code> will be used for the <code class="language-plaintext highlighter-rouge">Node.id</code>.</li> <li>A resolver to take the id and return the <code class="language-plaintext highlighter-rouge">Product</code>.</li> </ol> <p>So what is Hot Chocolate doing here?</p> <p>It’s adding a field <code class="language-plaintext highlighter-rouge">id</code> to the <code class="language-plaintext highlighter-rouge">Product</code> object the resolves to creating a globally unique identifier. It does this by encoding the type information into the id. For instance if internally our product id was the integer <code class="language-plaintext highlighter-rouge">42</code> then on our GraphQL service the id value will look something like <code class="language-plaintext highlighter-rouge">UHJvZHVjdAppNDI=</code> which decoded includes the type as well as the id. This also means that when the a query to the <code class="language-plaintext highlighter-rouge">node</code> field is resolved because the id contains the type information then it can route the query to the resolver in question.</p> <p>In later posts we’ll talk about connections and mutations.</p> Mon, 25 Jan 2021 00:00:00 +0000 http://compiledexperience.com/blog/posts/relay-node http://compiledexperience.com/blog/posts/relay-node [email protected] (Nigel Sampson) Custom GraphQL scalars <p>With the release of <a href="https://chillicream.com/blog/2020/11/23/hot-chocolate-11">Hot Chocolate 11</a> comes a very slimmed down approach to building custom scalars in GraphQL.</p> <h2 id="what-are-custom-scalars">What are custom scalars?</h2> <p>In GraphQL fields can be complex types such as objects, interfaces and lists or they can be scalar values. The <a href="https://spec.graphql.org/June2018/#sec-Scalars">GraphQL spec</a> lists all the built in scalars such as <code class="language-plaintext highlighter-rouge">ID</code>, <code class="language-plaintext highlighter-rouge">Int</code> and <code class="language-plaintext highlighter-rouge">String</code>. However we also have the ability to define our own custom scalars, we do this because we want to be able to take advantage of the strong type system that GraphQL provides. With custom scalars we can:</p> <ol> <li>Make invalid data for types an error before it even reaches your resolvers.</li> <li>Better express our domain model to clients.</li> <li>Enable client side type generation to be richer.</li> </ol> <h2 id="what-are-some-examples-of-custom-scalars">What are some examples of custom scalars</h2> <p>One of the standout examples is that the <a href="https://spec.graphql.org/June2018/#sec-Scalars">spec</a> doesn’t support any date / time scalar out of the box, we could use <code class="language-plaintext highlighter-rouge">String</code> but then we need to ensure it’s a valid string everywhere that takes an input and can potentially be harder to enforce a consistent output. While Hot Chocolate does come with a <code class="language-plaintext highlighter-rouge">DateTime</code> scalar we at Pushpay decided to model a scalar around the <code class="language-plaintext highlighter-rouge">Instant</code> from <a href="https://nodatime.org/">NodaTime</a> in order to make it every clear about how we’re dealing with things like timezones.</p> <p>Other examples could be for object identifiers, again the <a href="https://spec.graphql.org/June2018/#sec-Scalars">spec</a> comes with support for <code class="language-plaintext highlighter-rouge">ID</code> it can have its own problems. For instance we often see fields with argument signatures that look something like</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>type Query { communityMember(organizationID: ID! communityMemberID: ID!): CommunityMember } </code></pre></div></div> <p>It can be far too easy to put the wrong id into the wrong argument when all <code class="language-plaintext highlighter-rouge">ID</code> types are the same, with different types this becomes a validation error.</p> <h2 id="defining-our-custom-scalar">Defining our custom scalar</h2> <p>In Hot Chocolate 10 there was a decent amount of work to build a custom scalar, but now in <a href="https://chillicream.com/blog/2020/11/23/hot-chocolate-11">v11</a> it’s become a lot simpler. Let’s take a look at the code for our <code class="language-plaintext highlighter-rouge">Instant</code> custom scalar:</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">InstantType</span> <span class="p">:</span> <span class="n">ScalarType</span><span class="p">&lt;</span><span class="n">Instant</span><span class="p">,</span> <span class="n">StringValueNode</span><span class="p">&gt;</span> <span class="p">{</span> <span class="k">public</span> <span class="nf">InstantType</span><span class="p">()</span> <span class="p">:</span> <span class="k">base</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">Instant</span><span class="p">))</span> <span class="p">{</span> <span class="p">}</span> <span class="k">public</span> <span class="k">override</span> <span class="n">IValueNode</span> <span class="nf">ParseResult</span><span class="p">(</span><span class="kt">object</span><span class="p">?</span> <span class="n">resultValue</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">resultValue</span> <span class="k">switch</span> <span class="p">{</span> <span class="n">Instant</span> <span class="n">i</span> <span class="p">=&gt;</span> <span class="nf">ParseValue</span><span class="p">(</span><span class="n">i</span><span class="p">),</span> <span class="k">null</span> <span class="p">=&gt;</span> <span class="n">NullValueNode</span><span class="p">.</span><span class="n">Default</span><span class="p">,</span> <span class="n">_</span> <span class="p">=&gt;</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">SerializationException</span><span class="p">(</span><span class="s">"Could not parse Instant"</span><span class="p">,</span> <span class="k">this</span><span class="p">)</span> <span class="p">};</span> <span class="k">protected</span> <span class="k">override</span> <span class="n">Instant</span> <span class="nf">ParseLiteral</span><span class="p">(</span><span class="n">StringValueNode</span> <span class="n">valueSyntax</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">InstantPattern</span><span class="p">.</span><span class="n">ExtendedIso</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="n">valueSyntax</span><span class="p">.</span><span class="n">Value</span><span class="p">).</span><span class="n">Value</span><span class="p">;</span> <span class="k">protected</span> <span class="k">override</span> <span class="n">StringValueNode</span> <span class="nf">ParseValue</span><span class="p">(</span><span class="n">Instant</span> <span class="n">runtimeValue</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">StringValueNode</span><span class="p">(</span><span class="n">InstantPattern</span><span class="p">.</span><span class="n">ExtendedIso</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="n">runtimeValue</span><span class="p">));</span> <span class="p">}</span> </code></pre></div></div> <p>We’re obviously inheriting from <code class="language-plaintext highlighter-rouge">ScalarType</code> but what’s interesting is the two type arguments, the first is the underlying dotnet type for this scalar often referred to by the framework as the “runtime type”. The second argument is how this will be represented in parsed GraphQL. This can sound complex if we imagine passing our scalar as an argument without a variable what might it look like? For our <code class="language-plaintext highlighter-rouge">Instant</code> and the example below you can see we’ll be using a <code class="language-plaintext highlighter-rouge">StringValueNode</code>, I suspect a majority of custom scalars would be, but things like <code class="language-plaintext highlighter-rouge">IntValueNode</code> may also make sense.</p> <div class="language-graphql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">query</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">payments</span><span class="p">(</span><span class="n">after</span><span class="p">:</span><span class="w"> </span><span class="s2">"020-12-14T01:19:50Z"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">amount</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>In the constructor we pass the name our scalar to the base constructor. This affects the name in the final schema:</p> <div class="language-graphql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">scalar</span><span class="w"> </span><span class="n">Instant</span><span class="w"> </span></code></pre></div></div> <p>From there we define a couple of different parse methods to handle converting between the runtime value and the literal value in a way that should be reasonable obvious.</p> <h2 id="using-our-new-scalar">Using our new scalar</h2> <p>The best way to use our custom scalar is to add it directly to the schema.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">services</span><span class="p">.</span><span class="nf">AddGraphQLServer</span><span class="p">()</span> <span class="p">.</span><span class="n">AddType</span><span class="p">&lt;</span><span class="n">InstantType</span><span class="p">&gt;();</span> </code></pre></div></div> <p>Now any of our C# types that have <code class="language-plaintext highlighter-rouge">Instant</code> properties where Hot Chocolate is automatically disovering fields will now use our custom scalar.</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">Payment</span> <span class="p">{</span> <span class="k">public</span> <span class="n">Instant</span> <span class="n">OccurredOn</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> <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">Payment</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">occurredOn</span><span class="p">:</span><span class="w"> </span><span class="n">Instant</span><span class="p">!</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">scalar</span><span class="w"> </span><span class="n">Instant</span><span class="w"> </span></code></pre></div></div> Thu, 17 Dec 2020 00:00:00 +0000 http://compiledexperience.com/blog/posts/new-scalars http://compiledexperience.com/blog/posts/new-scalars [email protected] (Nigel Sampson) Securing a GraphQL endpoint <p>One of the first things we’ll typically want to do when building out any API whether its’ GraphQL or REST is to secure it against users who should not have access to it.</p> <p>I’ve seen a few articles about this recently when it comes to GraphQL but these are usually about securing the entire <code class="language-plaintext highlighter-rouge">/graphql</code> endpoing with a single authentication / authorization policy which isn’t very nuanced, especially given the nature of GraphQL.</p> <h2 id="defining-requirements">Defining requirements</h2> <p>Let’s start by definining our requirements for authentication and authorization.</p> <ol> <li>A valid JWT is required to be authenticated. This includes validating claims such as issuer and audience of the token.</li> <li>All queries / mutations (including introspection) must require authentication.</li> <li>The JWT token should contain scope claims.</li> <li>Some fields on our schema require specific scopes to access.</li> </ol> <h2 id="authentication">Authentication</h2> <p>Looking at the list above for authentication there’s nothing specific to GraphQL here so we should be able to use the out of the box ASP.NET Core functionality that looks something like:</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">services</span> <span class="p">.</span><span class="nf">AddAuthentication</span><span class="p">(</span><span class="n">JwtBearerDefaults</span><span class="p">.</span><span class="n">AuthenticationScheme</span><span class="p">)</span> <span class="p">.</span><span class="nf">AddJwtBearer</span><span class="p">(</span><span class="n">JwtBearerDefaults</span><span class="p">.</span><span class="n">AuthenticationScheme</span><span class="p">,</span> <span class="n">options</span> <span class="p">=&gt;</span> <span class="n">Configuration</span><span class="p">.</span><span class="nf">Bind</span><span class="p">(</span><span class="s">"JwtSettings"</span><span class="p">,</span> <span class="n">options</span><span class="p">));</span> </code></pre></div></div> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">app</span><span class="p">.</span><span class="nf">UseAuthentication</span><span class="p">();</span> </code></pre></div></div> <p>With the above we’re able to correctly authenticate users with the provided JWT token but we’re not yet applying it to any requests. Let’s start with fufilling requirement 2.</p> <h3 id="authenticating-all-queries--mutations">Authenticating all queries / mutations</h3> <p>Assuming we’re using <a href="https://hotchocolate.io/">Hot Chocolates</a> code first approach then we’ll simply call <code class="language-plaintext highlighter-rouge">Auhorize</code> with no parameters on the root query type. By providing no role or policy names we’re simply saying the user must be authenticated to access any fields on this type. Since this is the root query type we’re not authenticating all queries (including the introspection queries). We can complete a similar addition to the root mutation 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="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">Query</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">Authorize</span><span class="p">();</span> <span class="c1">// field definitions are here</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <h2 id="authorization">Authorization</h2> <p>Now we have working authentication let’s work on authorization, for the purposes of this example I’m going to assume our JWT includes some claims called <code class="language-plaintext highlighter-rouge">scope</code> which are the parts of the schema the authenticated user. We’ll assume our scope values look something like <code class="language-plaintext highlighter-rouge">document:read</code>, <code class="language-plaintext highlighter-rouge">document:write</code> and so on.</p> <p>We’re going to be using <a href="https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-5.0">policy based authorization</a>, so the first we’ll need to do is create some policys that enforce the scopes. In the example below we’re defining all our policies upfront, given the number of scopes we’re enforcing this is ok but as the list grows it may not be the best approach. In a later post I’ll write something up about dynamic policies.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">services</span><span class="p">.</span><span class="nf">AddAuthorization</span><span class="p">(</span><span class="n">options</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="n">options</span><span class="p">.</span><span class="nf">AddPolicy</span><span class="p">(</span><span class="s">"scope:document:read"</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="nf">RequireClaim</span><span class="p">(</span><span class="s">"scope"</span><span class="p">,</span> <span class="s">"document:read"</span><span class="p">));</span> <span class="n">options</span><span class="p">.</span><span class="nf">AddPolicy</span><span class="p">(</span><span class="s">"scope:document:write"</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="nf">RequireClaim</span><span class="p">(</span><span class="s">"scope"</span><span class="p">,</span> <span class="s">"document:write"</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="k">public</span> <span class="k">class</span> <span class="nc">MutationType</span> <span class="p">:</span> <span class="n">ObjectType</span><span class="p">&lt;</span><span class="n">Mutation</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">Mutation</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">Authorize</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">m</span> <span class="p">=&gt;</span> <span class="n">m</span><span class="p">.</span><span class="nf">CreateDocument</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">CreateDocumentInputType</span><span class="p">&gt;&gt;())</span> <span class="p">.</span><span class="n">Type</span><span class="p">&lt;</span><span class="n">DocumentType</span><span class="p">&gt;()</span> <span class="p">.</span><span class="nf">Authorize</span><span class="p">(</span><span class="s">"scope:document:write"</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>In the above code you can see us applying the <code class="language-plaintext highlighter-rouge">@authorize</code> diretive to the field <code class="language-plaintext highlighter-rouge">createDocument</code> and specifying the policy to enforce when the mutation is called.</p> <p>One question that comes up often is:</p> <blockquote> <p>If we’re enforcing the policy on the createDocument field what’s the point of the authentication on the mutation type?</p> </blockquote> <p>In the above example the type level directive doesn’t add any more security, but it allows to create a “secure by default” position. If another developer who hasn’t had their morning coffee comes along adds a new mutation to the type but forgets to add the polcy rules then at least the user but still be authenticated to call the mutation and it’s not open to the big bad internet.</p> <h3 id="multiple-policies">Multiple policies</h3> <p>One of the benefits to using policies and GraphQL is that we don’t need to just apply our policies on the root query and mutation fields, but use them across our entire 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">Document</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">title</span><span class="p">:</span><span class="w"> </span><span class="nb">String</span><span class="p">!</span><span class="w"> </span><span class="n">history</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="n">DocumentHistory</span><span class="p">!]</span><span class="w"> </span><span class="err">@</span><span class="n">authorize</span><span class="p">(</span><span class="n">policy</span><span class="p">:</span><span class="w"> </span><span class="err">"</span><span class="n">scope</span><span class="p">:</span><span class="n">document</span><span class="p">:</span><span class="n">read</span><span class="p">:</span><span class="n">sensitive</span><span class="err">"</span><span class="p">)</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>Here we’re apply a policy enforcing a policy requiring a narrower scope to a particular field on the document, so what would happen if a user had the scope <code class="language-plaintext highlighter-rouge">document:read</code> but not <code class="language-plaintext highlighter-rouge">document:read:sensitive</code> and tried to query the history?</p> <p>They would receive the data they are alloed to access such as id and title, but history would be null and the errors collection of the response would include an error referring to the history field.</p> Wed, 11 Nov 2020 00:00:00 +0000 http://compiledexperience.com/blog/posts/securing-graphql http://compiledexperience.com/blog/posts/securing-graphql [email protected] (Nigel Sampson) GraphQL Naming Conventions <p>The <a href="https://hotchocolate.io/">Hot Chocolate</a> GraphQL framework does an excellent job building a schema from your C# types by convention. An easy example would be to see a C# type such as:</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">User</span> <span class="p">{</span> <span class="k">public</span> <span class="kt">string</span> <span class="n">Username</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="k">public</span> <span class="kt">string</span><span class="p">?</span> <span class="n">Name</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="k">public</span> <span class="kt">bool</span> <span class="n">IsVerified</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> </code></pre></div></div> <p>and it will build a GraphQL object that looks 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">User</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">username</span><span class="p">:</span><span class="w"> </span><span class="nb">String</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="w"> </span><span class="n">isVerified</span><span class="p">:</span><span class="w"> </span><span class="nb">Boolean</span><span class="p">!</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>One point to notice is that differences in casing between C# properties and GraphQL field names, this is where we have a difference in naming / casing conventions between the two systems. Normally the out of the box conventions are fine but sometimes we may want to finesse them a bit. This could be because your company already has some conventions agreed upon, or you just plain don’t like the out of the box ones.</p> <p>For us at <a href="https://pushpay.com">Pushpay</a> one of the things we disagreed was the out of the box enum values convention. By default if you have an enum 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">enum</span> <span class="n">PaymentMethodType</span> <span class="p">{</span> <span class="n">CreditCard</span> <span class="n">ApplePay</span> <span class="n">AchBankAccount</span> <span class="p">}</span> </code></pre></div></div> <p>then <a href="https://hotchocolate.io/">Hot Chocolate</a> will create you a enum that looks like</p> <div class="language-graphql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">enum</span><span class="w"> </span><span class="n">PaymentMethodType</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">CREDITCARD</span><span class="w"> </span><span class="n">APPLEPAY</span><span class="w"> </span><span class="n">ACHBANKACCOUNT</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>Now if we look at the GraphQL specification concerning <a href="https://spec.graphql.org/June2018/#sec-Enums">enums</a> then it doesn’t really make it clear what multi word enum value casing should be. It’s only when we look at the enums that represent <a href="https://spec.graphql.org/June2018/#sec-Type-System.Directives">directive location</a> that we see the convention is what we affectionately call “Screaming Snake Case”.</p> <p>For us one of the other benefits of this change in case is how it affects TypeScript generation based off the schema where <code class="language-plaintext highlighter-rouge">CREDITCARD</code> becomes <code class="language-plaintext highlighter-rouge">Creditcard</code> but <code class="language-plaintext highlighter-rouge">CREDIT_CARD</code> becomes <code class="language-plaintext highlighter-rouge">CreditCard</code>.</p> <p>Now we could just go and hard wire these changes into all of our enum types but there’s also a chance one may be missed and fixing them after they’re in use is a painful topic for another blog post.</p> <p>Thankfully <a href="https://hotchocolate.io/">Hot Chcolate</a> has built in support for schema wide customization of naming conventions through the <code class="language-plaintext highlighter-rouge">INamingConventions</code> interface.</p> <p>To fix our example above we’ll make use of the <a href="https://github.com/Humanizr/Humanizer">Humanizer</a> 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">PushpayConventions</span> <span class="p">:</span> <span class="n">DefaultNamingConventions</span> <span class="p">{</span> <span class="k">public</span> <span class="k">override</span> <span class="n">NameString</span> <span class="nf">GetEnumValueName</span><span class="p">(</span><span class="kt">object</span> <span class="k">value</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="k">value</span><span class="p">.</span><span class="nf">ToString</span><span class="p">().</span><span class="nf">Underscore</span><span class="p">().</span><span class="nf">ToUpperInvariant</span><span class="p">();</span> <span class="p">}</span> </code></pre></div></div> <p>We simply register this class into our services as normal:</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">INamingConventions</span><span class="p">,</span> <span class="n">PushpayNamingConventions</span><span class="p">&gt;();</span> </code></pre></div></div> <p>Now we’ll get our “screaming snake case” for enums by default and no chance of one “falling through the cracks.</p> <p>I’d encourage you all to check out the entire interface and seeing else you may want to customize.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">interface</span> <span class="nc">INamingConventions</span> <span class="p">{</span> <span class="kt">string</span> <span class="nf">GetArgumentDescription</span><span class="p">(</span><span class="n">ParameterInfo</span> <span class="n">parameter</span><span class="p">);</span> <span class="n">NameString</span> <span class="nf">GetArgumentName</span><span class="p">(</span><span class="n">ParameterInfo</span> <span class="n">parameter</span><span class="p">);</span> <span class="kt">string</span> <span class="nf">GetEnumValueDescription</span><span class="p">(</span><span class="kt">object</span> <span class="k">value</span><span class="p">);</span> <span class="n">NameString</span> <span class="nf">GetEnumValueName</span><span class="p">(</span><span class="kt">object</span> <span class="k">value</span><span class="p">);</span> <span class="kt">string</span> <span class="nf">GetMemberDescription</span><span class="p">(</span><span class="n">MemberInfo</span> <span class="n">member</span><span class="p">,</span> <span class="n">MemberKind</span> <span class="n">kind</span><span class="p">);</span> <span class="n">NameString</span> <span class="nf">GetMemberName</span><span class="p">(</span><span class="n">MemberInfo</span> <span class="n">member</span><span class="p">,</span> <span class="n">MemberKind</span> <span class="n">kind</span><span class="p">);</span> <span class="kt">string</span> <span class="nf">GetTypeDescription</span><span class="p">(</span><span class="n">Type</span> <span class="n">type</span><span class="p">,</span> <span class="n">TypeKind</span> <span class="n">kind</span><span class="p">);</span> <span class="n">NameString</span> <span class="nf">GetTypeName</span><span class="p">(</span><span class="n">Type</span> <span class="n">type</span><span class="p">);</span> <span class="n">NameString</span> <span class="nf">GetTypeName</span><span class="p">(</span><span class="n">Type</span> <span class="n">type</span><span class="p">,</span> <span class="n">TypeKind</span> <span class="n">kind</span><span class="p">);</span> <span class="kt">bool</span> <span class="nf">IsDeprecated</span><span class="p">(</span><span class="n">MemberInfo</span> <span class="n">member</span><span class="p">,</span> <span class="k">out</span> <span class="kt">string</span> <span class="n">reason</span><span class="p">);</span> <span class="kt">bool</span> <span class="nf">IsDeprecated</span><span class="p">(</span><span class="kt">object</span> <span class="k">value</span><span class="p">,</span> <span class="k">out</span> <span class="kt">string</span> <span class="n">reason</span><span class="p">);</span> <span class="p">}</span> </code></pre></div></div> Thu, 08 Oct 2020 00:00:00 +0000 http://compiledexperience.com/blog/posts/graphlq-naming-conventions http://compiledexperience.com/blog/posts/graphlq-naming-conventions [email protected] (Nigel Sampson) Dynamic GraphQL Schemas <h2 id="declaring-our-schema">Declaring our schema</h2> <p>When we build a GraphQL service we define our schema, we typically do this via two different mechanisms. The first is often referred to as <em>schema first</em> where we declare the schema using the SDL that may look something like:</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>The the other approach is usually referred to as <em>code first</em> which will look very different depending on which GraphQL server framework you’re using, below is an example from the excellent .NET GraphQL framework <a href="https://hotchocolate.io/">Hot Chocolate</a>.</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 post isn’t going to debate the pros &amp; cons of the two above approaches, they both have strengths and weaknesses but I generally prefer <em>code first</em>. There’s dozens of articles about this on the internet if you want to read more.</p> <h2 id="a-surprising-benefit-of-the-code-first-approach">A surprising benefit of the code first approach</h2> <p>If you have gone and looked at some of the articles discussing the <em>code first</em> approach one thing may strike you, it’s a very declarative approach, we’re using code to create the schema but we’re still creating new code when new types come along. The question then becomes, is this always the case?</p> <h3 id="strongly-typed-explosion">Strongly typed explosion</h3> <p>Recently at <a href="https://pushpay.com">Pushpay</a> I spiked some work on what a Notifications microservice would look like, a simple service that allowed other services to push notifications for users and allow applications to query the notifications for a specific user.</p> <p>Obviously different notifications will have different metadata associated with that notification based on what sort of notification it is. When an export job has been completed processing then we’ll need the id of the export and potentially a link to the result.</p> <p>It can be very easy when designing the GraphQL schema for this service to try and create a generic schema that can acoomodate all sorts of different types of notifications. Ultimately you’ll end up with something that looks 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">Notification</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">key</span><span class="p">:</span><span class="w"> </span><span class="n">NotificationKey</span><span class="p">!</span><span class="w"> </span><span class="n">createdOn</span><span class="p">:</span><span class="w"> </span><span class="n">Instant</span><span class="p">!</span><span class="w"> </span><span class="n">metadata</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="n">MetadataItem</span><span class="p">!]!</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">MetadataItem</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">key</span><span class="p">:</span><span class="w"> </span><span class="nb">String</span><span class="p">!</span><span class="w"> </span><span class="n">value</span><span class="p">:</span><span class="w"> </span><span class="nb">String</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>While functional in my opinion it misses the point of GraphQL, why bother trying to build a generic not very type safe model on top of a strongly typed schema? It’s impossible to see from the schema what sort of notifications we receive nor what metadata they’ll have. My ideal schema would look something like:</p> <div class="language-graphql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">interface</span><span class="w"> </span><span class="n">Notification</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">key</span><span class="p">:</span><span class="w"> </span><span class="n">NotificationKey</span><span class="p">!</span><span class="w"> </span><span class="n">createdOn</span><span class="p">:</span><span class="w"> </span><span class="n">Instant</span><span class="p">!</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">ExportCompleted</span><span class="w"> </span><span class="k">implements</span><span class="w"> </span><span class="n">Notification</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">key</span><span class="p">:</span><span class="w"> </span><span class="n">NotificationKey</span><span class="p">!</span><span class="w"> </span><span class="n">createdOn</span><span class="p">:</span><span class="w"> </span><span class="n">Instant</span><span class="p">!</span><span class="w"> </span><span class="n">exportKey</span><span class="p">:</span><span class="w"> </span><span class="nb">String</span><span class="p">!</span><span class="w"> </span><span class="n">sucessful</span><span class="p">:</span><span class="w"> </span><span class="nb">Boolean</span><span class="p">!</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>Now this is where the “type explosion” I mentioned in the header for this section comes in. When I first started spiking what a fully strongly typed schema would look like I really had examine what each notification added to the schema it it’s entirety and came to the following list. Each notfication type would require (we’ll use a complete export for concrete examples):</p> <ol> <li>The notification type itself that implements the interface <code class="language-plaintext highlighter-rouge">ExportCompleted</code>.</li> <li>A mutation field that creates that notification. <div class="language-graphql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">createExportCompletedNotification(</span><span class="k">input</span><span class="err">:</span><span class="w"> </span><span class="n">CreateExportCompletedInput</span><span class="p">!</span><span class="err">):</span><span class="w"> </span><span class="n">ExportCompletedResult</span><span class="p">!</span><span class="w"> </span></code></pre></div> </div> </li> <li>A input to the above mutation that includes the metadata specific to that notification. <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>input CreateExportCompletedInput { exportKey: String! sucessful: Boolean! # common notification fields such as who it's for here } </code></pre></div> </div> </li> <li>A result class of that mutation that handles returning the strongly typed mutation payload. <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">ExportCompletedCreated</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">notification</span><span class="p">:</span><span class="w"> </span><span class="n">ExportCompleted</span><span class="p">!</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div> </div> </li> <li>A union of the above payload and validation errors (it’s a post for another day but we represent validation errors in the schema). <div class="language-graphql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">union</span><span class="w"> </span><span class="n">ExportCompletedResult</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">ExportCompletedCreated</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="n">ValidationErrors</span><span class="w"> </span></code></pre></div> </div> </li> </ol> <p>Not a huge list but the idea that every developer that wants to add a new notification type has to create five different types and a new mutation isn’t attractive, it’s high friction and quite a waste of time.</p> <p>Rather than using declarative code first can we dynamically create the above types without requireing a mountain of work?</p> <h3 id="defining-the-definition">Defining the definition</h3> <p>First we start with our “source of truth” for nofications, below is a stripped down example of a <code class="language-plaintext highlighter-rouge">NotificationDefinition</code> class. This is the declarative piece of the puzzle that provides the data for the type generation below, for your needs it could come from anywhere including a database.</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">NotificationDefinition</span> <span class="p">{</span> <span class="k">readonly</span> <span class="n">List</span><span class="p">&lt;(</span><span class="kt">string</span><span class="p">,</span> <span class="n">Type</span><span class="p">,</span> <span class="kt">bool</span><span class="p">)&gt;</span> <span class="n">_data</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;(</span><span class="kt">string</span><span class="p">,</span> <span class="n">Type</span><span class="p">,</span> <span class="kt">bool</span><span class="p">)&gt;();</span> <span class="k">public</span> <span class="k">static</span> <span class="k">readonly</span> <span class="n">IReadOnlyCollection</span><span class="p">&lt;</span><span class="n">NotificationDefinition</span><span class="p">&gt;</span> <span class="n">All</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">NotificationDefinition</span><span class="p">&gt;</span> <span class="p">{</span> <span class="k">new</span> <span class="nf">NotificationDefinition</span><span class="p">(</span><span class="s">"ExportCompleted"</span><span class="p">)</span> <span class="p">.</span><span class="n">WithMetadata</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;(</span><span class="s">"ExportKey"</span><span class="p">,</span> <span class="n">required</span><span class="p">:</span> <span class="k">true</span><span class="p">)</span> <span class="p">.</span><span class="n">WithMetadata</span><span class="p">&lt;</span><span class="kt">bool</span><span class="p">&gt;(</span><span class="s">"Successful"</span><span class="p">,</span> <span class="n">required</span><span class="p">:</span> <span class="k">true</span><span class="p">)</span> <span class="p">};</span> <span class="k">public</span> <span class="nf">NotificationDefinition</span><span class="p">(</span><span class="kt">string</span> <span class="n">name</span><span class="p">)</span> <span class="p">{</span> <span class="n">Name</span> <span class="p">=</span> <span class="n">name</span><span class="p">.</span><span class="nf">Pascalize</span><span class="p">();</span> <span class="p">}</span> <span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span> <span class="k">public</span> <span class="n">IReadOnlyCollection</span><span class="p">&lt;(</span><span class="kt">string</span><span class="p">,</span> <span class="n">Type</span><span class="p">,</span> <span class="kt">bool</span><span class="p">)&gt;</span> <span class="n">Metadata</span> <span class="p">=&gt;</span> <span class="n">_data</span><span class="p">;</span> <span class="k">public</span> <span class="n">NotificationDefinition</span> <span class="n">WithMetadata</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="kt">string</span> <span class="n">name</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">required</span><span class="p">)</span> <span class="p">{</span> <span class="n">_data</span><span class="p">.</span><span class="nf">Add</span><span class="p">((</span><span class="n">name</span><span class="p">.</span><span class="nf">Camelize</span><span class="p">(),</span> <span class="k">typeof</span><span class="p">(</span><span class="n">T</span><span class="p">),</span> <span class="n">required</span><span class="p">));</span> <span class="k">return</span> <span class="k">this</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>Now instead of trying to declare our types in code we’re going to write some code that uses this definition and creates the types for us. This code is going to be very <a href="https://hotchocolate.io/">Hot Chocolate</a> specific but hopefully the concept applies across other frameworks just as well. Here’s we’re iterating over our definitions and creating a new <code class="language-plaintext highlighter-rouge">ObjectType</code>, giving it the correct name, implementing the interface and then adding the correct fields based on the common fields and then the specific fields for that definition. The last step is then registering that type within the schema.</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">ISchemaConfiguration</span> <span class="nf">RegisterNotificationTypes</span><span class="p">(</span><span class="k">this</span> <span class="n">ISchemaConfiguration</span> <span class="n">configuration</span><span class="p">,</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">NotificationDefinition</span><span class="p">&gt;</span> <span class="n">definitions</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">definition</span> <span class="k">in</span> <span class="n">definitions</span><span class="p">)</span> <span class="p">{</span> <span class="kt">var</span> <span class="n">notificationTypeName</span> <span class="p">=</span> <span class="n">definition</span><span class="p">.</span><span class="n">Name</span><span class="p">;</span> <span class="kt">var</span> <span class="n">notificationType</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">ObjectType</span><span class="p">(</span><span class="n">d</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="n">d</span><span class="p">.</span><span class="nf">Name</span><span class="p">(</span><span class="n">notificationTypeName</span><span class="p">).</span><span class="n">Implements</span><span class="p">&lt;</span><span class="n">NotificationType</span><span class="p">&gt;();</span> <span class="n">d</span><span class="p">.</span><span class="nf">Field</span><span class="p">(</span><span class="s">"key"</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">NotificationKeyType</span><span class="p">&gt;&gt;().</span><span class="nf">Resolver</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">Parent</span><span class="p">&lt;</span><span class="n">Notification</span><span class="p">&gt;().</span><span class="n">Key</span><span class="p">);</span> <span class="n">d</span><span class="p">.</span><span class="nf">Field</span><span class="p">(</span><span class="s">"createdOn"</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">InstantType</span><span class="p">&gt;&gt;().</span><span class="nf">Resolver</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">Parent</span><span class="p">&lt;</span><span class="n">Notification</span><span class="p">&gt;().</span><span class="n">CreatedOn</span><span class="p">);</span> <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">type</span><span class="p">,</span> <span class="n">required</span><span class="p">)</span> <span class="k">in</span> <span class="n">definition</span><span class="p">.</span><span class="n">Metadata</span><span class="p">)</span> <span class="p">{</span> <span class="kt">var</span> <span class="n">fieldType</span> <span class="p">=</span> <span class="n">required</span> <span class="p">?</span> <span class="p">(</span><span class="n">ITypeNode</span><span class="p">)</span> <span class="k">new</span> <span class="nf">NonNullTypeNode</span><span class="p">(</span><span class="k">new</span> <span class="nf">NamedTypeNode</span><span class="p">(</span><span class="n">type</span><span class="p">.</span><span class="n">Name</span><span class="p">))</span> <span class="p">:</span> <span class="k">new</span> <span class="nf">NamedTypeNode</span><span class="p">(</span><span class="n">type</span><span class="p">.</span><span class="n">Name</span><span class="p">);</span> <span class="n">d</span><span class="p">.</span><span class="nf">Field</span><span class="p">(</span><span class="n">name</span><span class="p">)</span> <span class="p">.</span><span class="nf">Type</span><span class="p">(</span><span class="n">fieldType</span><span class="p">)</span> <span class="p">.</span><span class="nf">Resolver</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">Parent</span><span class="p">&lt;</span><span class="n">Notification</span><span class="p">&gt;().</span><span class="n">Metadata</span><span class="p">[</span><span class="n">name</span><span class="p">]);</span> <span class="p">}</span> <span class="p">});</span> <span class="n">configuration</span><span class="p">.</span><span class="nf">RegisterType</span><span class="p">(</span><span class="n">notificationType</span><span class="p">);</span> <span class="p">}</span> <span class="k">return</span> <span class="n">configuration</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <p>We won’t show creating all five pieces listed above, most follow similar patterns to the above code. The one interesting one is the mutation field. Here we’ll register an <code class="language-plaintext highlighter-rouge">ObjectTypeExtension</code> that extends our <code class="language-plaintext highlighter-rouge">MutationType</code> definined elsewhere. This means we can keep the whole definition self contained.</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">configuration</span><span class="p">.</span><span class="nf">RegisterType</span><span class="p">(</span><span class="k">new</span> <span class="nf">ObjectTypeExtension</span><span class="p">(</span><span class="n">d</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="n">d</span><span class="p">.</span><span class="nf">Name</span><span class="p">(</span><span class="s">"Mutation"</span><span class="p">);</span> <span class="n">d</span><span class="p">.</span><span class="nf">Field</span><span class="p">(</span><span class="s">$"create</span><span class="p">{</span><span class="n">notificationTypeName</span><span class="p">}</span><span class="s">Notification"</span><span class="p">)</span> <span class="p">.</span><span class="nf">Type</span><span class="p">(</span><span class="k">new</span> <span class="nf">NonNullTypeNode</span><span class="p">(</span><span class="k">new</span> <span class="nf">NamedTypeNode</span><span class="p">(</span><span class="n">mutationResultTypeName</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="nf">Type</span><span class="p">(</span><span class="k">new</span> <span class="nf">NonNullTypeNode</span><span class="p">(</span><span class="k">new</span> <span class="nf">NamedTypeNode</span><span class="p">(</span><span class="n">mutationInputTypeName</span><span class="p">))))</span> <span class="p">.</span><span class="nf">Resolver</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">Resolver</span><span class="p">&lt;</span><span class="n">Mutation</span><span class="p">&gt;().</span><span class="nf">Create</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">definition</span><span class="p">));</span> <span class="p">}));</span> </code></pre></div></div> <h2 id="summary">Summary</h2> <p>A lot of the code above isn’t neccesary to understand, the idea we’re trying to get across here is that we can use <em>code first</em> approach lets us not just use a declarative schema but build ourselves a data driven schema that’s built at start up.</p> Thu, 10 Sep 2020 00:00:00 +0000 http://compiledexperience.com/blog/posts/dynamic-graphql http://compiledexperience.com/blog/posts/dynamic-graphql [email protected] (Nigel Sampson) Stepping away from Caliburn.Micro <h2 id="background">Background</h2> <p>This has been a hard post to write and probably a few years over due, but human nature being what it is has delayed it longer than it should have.</p> <p>Roughly eight years ago I started working at a company Marker Metro that was founded around building great Windows apps both on the desktop and phone. This was about the time of the Windows 8 launch, we were fortunate to be working with a number of launch partners and have access to early builds of Windows 8 in order to have apps in the store when the OS launched. Back then I had used Caliburn.Micro (at the time created and maintained by Rob Eisenberg) on some WPF, Silverlight and WP7 projects and wanted to use it in Windows 8. By then Rob had moved on to working mostly with Durandal so I spent some of my time at work porting CM across to Windows 8 and submitted the PR.</p> <p>These PR’s ultimately led to me to take over maintenance of the CM project and over time add support for UWP and Xamarin.Forms. I’ve been very lucky with this opportunity, maintaining a popular open source project lead to many other opportunities that really snowballed, being able to speak internationally as well as the award of a Microsoft MVP for five consecutive years. I was also lucky in that my work at the time supported working on Caliburn.Micro as part of my day job.</p> <p>But four years ago I moved on to a different job that wasn’t apps focused and the only time I had to work on Caliburn.Micro was late in the evening. At this point my family had grown and spare time was even more precious, for a long time I thought I could keep it up and be able to support the project even when I wasn’t using it on a day to day basis in my evenings. History has shown that it wasn’t going to happen, the time for 4.0 to be created and released is ample proof of that and I’ve put off writing this post for probably two years.</p> <h2 id="what-now">What now?</h2> <p>Over the next few weeks I’ll be doing the following.</p> <ol> <li>Publishing a release candidate of 4.0 and as long as nothing crazy comes up will be published as the final version. I’m a little undecided on the last part as the documentation will be very out of date at that point and this may cause more confusion that it’s worth.</li> <li>I’ll continue to pay for the domain so the website won’t disappear but will be updated to reflect the state of the project.</li> <li>If someone is willing to step up and help maintain the project reach out and we can discuss a transition plan.</li> </ol> <p>I’d like to thank everyone who’s helped me with this project over the years.</p> Thu, 18 Jun 2020 00:00:00 +0000 http://compiledexperience.com/blog/posts/Stepping-Away http://compiledexperience.com/blog/posts/Stepping-Away [email protected] (Nigel Sampson)