-
Notifications
You must be signed in to change notification settings - Fork 162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Modify federation to be possible with edn schema as well #420
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the initial effort! I've outlined some improvements that could be made.
deps.edn
Outdated
@@ -1,7 +1,8 @@ | |||
{:deps {org.clojure/clojure {:mvn/version "1.10.3"} | |||
clj-antlr/clj-antlr {:mvn/version "0.2.12"} | |||
org.flatland/ordered {:mvn/version "1.15.10"} | |||
org.clojure/data.json {:mvn/version "2.4.0"}} | |||
org.clojure/data.json {:mvn/version "2.4.0"} | |||
org.clojure/core.match {:mvn/version "1.0.0"}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at how match
is used in the code, it feels a bit like over kill - most of the cases could be covered with cond
or condp
, and adding dependencies is always an issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed match based on review.
dev-resources/edn-federation.sdl
Outdated
@@ -0,0 +1,34 @@ | |||
type _Service{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we could get pretty-printed output. It would make things more complicated, for sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bit complicated, but I'll try.
[description] | ||
(if (nil? description) | ||
"" | ||
(str "\"\"\"\n" description "\n\"\"\"\n"))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if description itself includes characters, such as "
, that need to be escaped?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The input is edn, so there seems to be no problem. Can you give me an example?
(defn ^:private value->string | ||
[value] | ||
(match value | ||
(string :guard string?) (str "\"" string "\"") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, may need to escape some characters.
(edn-directives->sdl-directives directives) | ||
(edn-fields->sdl-fields fields)))) | ||
(join "\n"))) | ||
(defn ^:private edn-queries->sdl-queries |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer a blank line between def
forms. There's a couple of cases of this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:queries is such a hold over from early days Lacinia; we really should deprecate it, it causes problems.
That being said, should probably fold :queries in the Query object (likewise mutations and subscriptions) and then pretty print that.
It may be ok to skimp on some error checking, such as name collisions between Query fields an names in the :queries map ... incorrect SDL will be generated BUT that will be caught an instant later at schema compilation and/or calls to prevent-collision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edited to reflect the review.
(defn ^:private edn-scalars->sdl-scalars | ||
[scalars] | ||
(->> (keys scalars) | ||
(map name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's value to sorting by name in each of these blocks, for repeatability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorting has been added to reflect reviews.
"Translate the edn lacinia schema to the SDL schema." | ||
[schema] | ||
(->> schema | ||
(map (fn [[key val]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, repeatability; depending on the type of map (array-map vs. hash-map, etc.) this order of all this could shift dramatically; for small maps it's in order of keys added, in larger maps (hash-map) it's related to the hash of the key.
I'd say a good fixed order would be:
- directive defs
- scalars
- enums
- unions
- interfaces
- input-objects
- objects (including Query, Mutation, etc., rolled in)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorting has been added to reflect reviews.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another problem I'm just noticing is that the extras directives, types, etc. provided by com.walmartlabs.lacinia.federation/foundation-types need to be filtered back out.
:mutations (edn-mutations->sdl-mutations val) | ||
:enums (edn-enums->sdl-enums val) | ||
:directive-defs (edn-directive-defs->sdl-directives val) | ||
:roots ""))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Roots could be important, though my experience with Apollo is that it's fragile if the service schemas don't agree on the names of the root objects. That may have changed since I looked at it > 1 year ago.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I don't understand what you mean, can you elaborate on that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the SDL you can override the default names of the Query, Mutation, and Subscription objects using the schema
keyword (https://spec.graphql.org/October2021/#sec-Schema).
So this code must honor that, but must also (as necessary) emit the schema
content into the output SDL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I didn't know there was such a grammar. reflected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I understand the Apollo federation documentation, the @extends and other directive, scalar types, etc. are not explicit in the input SDL but still available, and should not be generated into the output SDL. In the systems I've developed under federation, we start with SDL document and provided that document, as is ... you can see that in the code in master.
(nil? hd) "" | ||
(= 'non-null hd) (str (apply-list edn-type->sdl-type tl) "!") | ||
(= 'list hd) (str "[" (apply-list edn-type->sdl-type tl) "]") | ||
(= 'String hd) "String" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These special cases would also be caught by the symbol?
case below.
"Translate the edn lacinia schema to the SDL schema." | ||
[schema] | ||
(->> schema | ||
(map (fn [[key val]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another problem I'm just noticing is that the extras directives, types, etc. provided by com.walmartlabs.lacinia.federation/foundation-types need to be filtered back out.
I am not sure if I understand the review comment well, but if you look at the document about apollo's federation subgraph, it says that the server should provide some schemas. Then shouldn't the schema be exposed to the router through query{_service{sdl}}}? |
I'll work on resolving this against the Apollo documentation. |
Federation is a moving target, we're compatible with 1.0, but 2.0 changes things: https://www.apollographql.com/docs/federation/federated-types/federated-directives In any case, I don't think the current version of this code will work because it includes the Federation (1.0) annotations and objects injected into the schema. In the current version, based on reading SDL, you can see that those elements aren't in the input SDL (they are provided after that is parsed) and then we use the input SDL as-is when exposing the schema to the Apollo gateway. I don't know exactly what would happen if we include those elements in the generated SDL but I suspect Apollo will reject the schema. |
I'm struggling to find time to come back and finish reviewing this. I want to test what happens when the injected Fed 1.0 directives and types are not filtered out when generating SDL from the schema (that is, see how Apollo reacts). |
Extends lacinia's support for federation to add the ability to support federation through the edn schema as well.