Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Modify federation to be possible with edn schema as well #420

Open
wants to merge 19 commits into
base: master
Choose a base branch
from

Conversation

hmy3743
Copy link

@hmy3743 hmy3743 commented Aug 8, 2022

Extends lacinia's support for federation to add the ability to support federation through the edn schema as well.

@CLAassistant
Copy link

CLAassistant commented Aug 8, 2022

CLA assistant check
All committers have signed the CLA.

Copy link
Member

@hlship hlship left a 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"}}
Copy link
Member

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.

Copy link
Author

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.

@@ -0,0 +1,34 @@
type _Service{
Copy link
Member

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.

Copy link
Author

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")))
Copy link
Member

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?

Copy link
Author

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 "\"")
Copy link
Member

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
Copy link
Member

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.

Copy link
Member

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.

Copy link
Author

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)
Copy link
Member

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.

Copy link
Author

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]]
Copy link
Member

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)

Copy link
Author

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.

Copy link
Member

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 "")))
Copy link
Member

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.

Copy link
Author

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?

Copy link
Member

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.

Copy link
Author

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.

Copy link
Member

@hlship hlship left a 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"
Copy link
Member

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]]
Copy link
Member

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.

@hmy3743
Copy link
Author

hmy3743 commented Aug 28, 2022

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.

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}}}?

@hlship
Copy link
Member

hlship commented Aug 28, 2022

I'll work on resolving this against the Apollo documentation.

@hlship
Copy link
Member

hlship commented Sep 2, 2022

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.

@hlship
Copy link
Member

hlship commented Dec 1, 2022

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).

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

Successfully merging this pull request may close these issues.

3 participants