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

Support open ended scalars #1069

Merged

Conversation

kdawgwilk
Copy link
Contributor

@kdawgwilk kdawgwilk commented May 11, 2021

This adds support for defining open_ended scalars that can recieve arbitrary GQL data. We opted to put this behavior behind a flag so that it is an opt-in feature. This was a necessary feature to support the Federation spec's _Any scalar

lib/absinthe/phase/document/arguments/flag_invalid.ex Outdated Show resolved Hide resolved
lib/absinthe/phase/document/arguments/parse.ex Outdated Show resolved Hide resolved
test/absinthe/execution/arguments_test.exs Outdated Show resolved Hide resolved
test/support/fixtures/arguments_schema.ex Show resolved Hide resolved
test/support/fixtures/arguments_schema.ex Outdated Show resolved Hide resolved
@kdawgwilk
Copy link
Contributor Author

Example implementations of the Federation _Any type that is used as a scalar object

All of them appear to just forward the input object type through and don't really do any coercion so I would expect absinthe to also do something similar

lib/absinthe/phase/document/arguments/parse.ex Outdated Show resolved Hide resolved
lib/absinthe/phase/schema.ex Outdated Show resolved Hide resolved
lib/absinthe/phase/schema.ex Outdated Show resolved Hide resolved
test/support/fixtures/arguments_schema.ex Outdated Show resolved Hide resolved
@benwilson512
Copy link
Contributor

As a note for others reading the PR: Much as Absinthe has resisted this feature over the years, it isn't a fight we're gonna win. I think it's time to try to support this.

@binaryseed
Copy link
Contributor

We have an implementation of this in our API with an allow-list for scalars...

So one question is how we want this to be enabled - will it be on by default? opt-in per scalar? something else?

@binaryseed
Copy link
Contributor

We needed to modify:

  • Absinthe.Phase.Document.Arguments.Data
  • Absinthe.Phase.Document.Arguments.FlagInvalid
  • Absinthe.Phase.Document.Arguments.Parse

@kdawgwilk
Copy link
Contributor Author

kdawgwilk commented May 13, 2021

We have an implementation of this in our API with an allow-list for scalars...

@binaryseed can you point me in the right direction to look into this implementation? Was it previous PR that didnt get merged? Just realized you meant in your internal API this was implemented.

@kdawgwilk
Copy link
Contributor Author

Which approach do we want to take on this?

  1. create fake schema nodes in the Phase.Schema for Input.Fields when we are unable to find an existing schema node?
    changes would like be here
    defp set_schema_node(%Blueprint.Input.Field{} = node, parent, schema, adapter) do
    case node.name do
    "__" <> _ ->
    %{node | schema_node: nil}
    name ->
    %{node | schema_node: find_schema_field(parent.schema_node, name, schema, adapter)}
    end
    end
    to do a find_or_create_schema_field
  2. handle Document.Arguments with schema_node: nil by not marking them as invalid anymore, changes would likely be made here
    defp handle_node(%{schema_node: nil, flags: %{}} = node) do
    node |> flag_invalid(:extra)
    end

    I am leaning towards option 2 since generating fake schema_nodes seems like a bad idea

@benwilson512
Copy link
Contributor

I think maybe an option 3, where you pattern match for %{schema_node: %Type.Scalar{}} = node and then return {:halt, node} which will prevent further descent in the tree.

@kdawgwilk
Copy link
Contributor Author

@benwilson512 The FlagInvalid phase is a postwalk so halting wouldn't help us right?

@kdawgwilk kdawgwilk force-pushed the kaden/object_scalars branch 3 times, most recently from 6c96487 to 9e6646a Compare May 21, 2021 19:23
lib/absinthe/phase/document/arguments/flag_invalid.ex Outdated Show resolved Hide resolved
lib/absinthe/phase/document/arguments/parse.ex Outdated Show resolved Hide resolved
test/absinthe/execution/arguments/scalar_test.exs Outdated Show resolved Hide resolved
test/absinthe/execution/arguments/scalar_test.exs Outdated Show resolved Hide resolved
test/support/fixtures/arguments_schema.ex Show resolved Hide resolved
@kdawgwilk kdawgwilk marked this pull request as ready for review May 21, 2021 19:25
@kdawgwilk
Copy link
Contributor Author

Alright I am to a place where I am feeling pretty good about the implementation now and would love some more thorough reviews

Copy link
Contributor

@benwilson512 benwilson512 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm mostly happy with this, but we need to review spec behavior for scalars that don't accept struct literals, and we need a number of additional test cases to more comprehensively cover the new functionality.

test/absinthe/execution/arguments_test.exs Show resolved Hide resolved
test/absinthe/execution/arguments/scalar_test.exs Outdated Show resolved Hide resolved
@binaryseed
Copy link
Contributor

This may require that the scalars have some sort of explicit flag.

Agreed, I think this needs to be opt-in only, and per-scalar. An API will want both Scalars that are open-ended and some that aren't. Out of the box, the addition of this feature should not change any existing behavior.

@kdawgwilk
Copy link
Contributor Author

Any ideas on what we want the opt-in API to look like?

scalar :any do
  meta :dynamic, true
  ...
end

@binaryseed
Copy link
Contributor

I think we may need a new syntax / field, since we should probably leave meta for usage by the end user & not Absinthe.

I tend to like calling these "open ended", what do you think @benwilson512

@benwilson512
Copy link
Contributor

@binaryseed agreed, I'd be fine with like:

scalar :any, open_ended: true do
  # stuff
end

@kdawgwilk kdawgwilk changed the title Support passing through object scalars Support open ended scalars May 25, 2021
@kdawgwilk
Copy link
Contributor Author

I am happy to make more changes but I think I have addressed the feedback so far

@kdawgwilk
Copy link
Contributor Author

Is this something that would make sense to live in absinthe? Or would it be better as some sort of plugin to absinthe? I need this feature for my use case of federation but I may be able to put this behavior into the absinthe_federation library itself if we dont want this to be a core feature of absinthe. Would love some direction on this so I can move forward with whatever the team thinks is best for the absinthe ecosystem

@benwilson512
Copy link
Contributor

The test cases look largely correct to me now, I'll review in more detail later this week. I think supporting it this way in Absinthe is important since it needed changes to key files.

@kdawgwilk
Copy link
Contributor Author

Once this merges can we release it as an RC? I would like to get the absinthe_federation library on hex but it will depend on these changes. I would also like to submit a talk proposal to Elixir conf to talk about federation and those submissions are due July 11th, 2021

@benwilson512 benwilson512 merged commit 1948513 into absinthe-graphql:master Jun 15, 2021
@benwilson512
Copy link
Contributor

These changes look good! Will look at doing a release shortly.

@dylan-chong
Copy link
Contributor

Thanks so much for this PR @kdawgwilk !

@benwilson512 At Solve, we were looking at using this feature for a long time. I've got an open PR to use this new feature, but I just wanted to double check if this is a feature that will continue to be supported

@benwilson512
Copy link
Contributor

Yes, this will be supported.

@razielgn
Copy link

Was there a specific reason for not supporting open ended scalars that are lists? I have data about fixed-sized arrays (like Vec3). Having list_of(:float) feels too generic, while handling their validation at schema level, plus providing exact documentation using a scalar would be very useful.
If it doesn't go against GraphQL's spec, I could make an attempt at submitting a PR.

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

Successfully merging this pull request may close these issues.

5 participants