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

Fix value type behavior within composition and execution #3182

Merged
merged 16 commits into from
Aug 27, 2019

Conversation

trevor-scheer
Copy link
Member

@trevor-scheer trevor-scheer commented Aug 21, 2019

This PR addresses my oversights in the original value type implementation here:
#3063

This PR implements value types as a core part of composition
and query planning. With this work, we now:

  • build another metadata map for tracking value types
  • attach value type metadata to schema type nodes
  • make query planning decisions based on value type metadata

Fixes #3141 (see for more context)

Copy link
Contributor

@jbaxleyiii jbaxleyiii left a comment

Choose a reason for hiding this comment

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

@trevor-scheer I think your comment on a different approach is the right direction. Even though removing value types from the type map is a quicker approach, I think it may lead to confusion down the road. I'd rather us model the value type directly. Something akin to isSharedValueType or even creating a list of all services that share the value type would allow us to better compute the right query plan and give better errors when the planner reaches an unexpected state. Let's take another revision of this including more time on the test suite to model the possible interactions

expect.addSnapshotSerializer(astSerializer);
expect.addSnapshotSerializer(queryPlanSerializer);

describe('value types', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

It will take more time, but I think we should verify all types of ValueTypes in this test suite instead of just a single object type. We would be remiss if we made a change that broke unions by accident without a strong test coverage.

packages/apollo-gateway/src/buildQueryPlan.ts Show resolved Hide resolved
@trevor-scheer trevor-scheer force-pushed the trevor/value-types-bug branch from ba1da07 to 3818c6b Compare August 23, 2019 21:22
@trevor-scheer trevor-scheer marked this pull request as ready for review August 23, 2019 21:31
Copy link
Contributor

@jbaxleyiii jbaxleyiii left a comment

Choose a reason for hiding this comment

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

@trevor-scheer I've left a few questions about the actual implementation here, but I think the result is the one for which we are looking!

I do notice we are doing a lot of accessing the last element in the serviceNames array. I wonder if there is a better way to represent the "winning" service here?

);

const isValueType =
name.length === 0 &&
Copy link
Contributor

Choose a reason for hiding this comment

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

Are name and kind arrays here? Why are you checking length?

Copy link
Member Author

@trevor-scheer trevor-scheer Aug 27, 2019

Choose a reason for hiding this comment

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

They are - in the case that they're different, the diffing function returns both names in an array which is useful for messaging. I've written a function to hide the impl. details of diffing for simple use cases like this one.

fd4709e

* A map for tracking which types have been determined to be a value type, a type
* shared across at least 2 services.
*/
export interface ValueTypesMap {
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably would be better to make this a Set since it isn't a map that stores anything.

Copy link
Member Author

@trevor-scheer trevor-scheer Aug 27, 2019

Choose a reason for hiding this comment

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

💯 67a4478

packages/apollo-federation/src/composition/compose.ts Outdated Show resolved Hide resolved
...(keyDirectivesMap[typeName] && {
keys: keyDirectivesMap[typeName],
}),
isValueType: Boolean(valueTypesMap[typeName]),
Copy link
Contributor

Choose a reason for hiding this comment

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

If this was a Set, you could write this as valueTypesSet.has(typename)

@@ -343,7 +378,7 @@ export function addFederationMetadataToSchemaNodes({
) {
field.federation = {
...field.federation,
serviceName: baseServiceName,
serviceName: serviceNames && serviceNames[serviceNames.length - 1],
Copy link
Contributor

Choose a reason for hiding this comment

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

hmm, so in this case the last service in the list wins as the serviceName for the federation field? Does this still apply for value types?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is a good point, and made me consider the effect and validity of @provides on value type fields.

Resolved in 053d1a3

This is a first attempt at completing the implementation of value
types. This implementation is very inline with a suggestion from
a contributor (@victorandree) that can be found here:
#3141 (comment)

The solution is relatively straightforward, though a bit hacky.
After the maps are constructed, we go back and "clean them up"
by identifying value types in the map and removing any signs of
service ownership from them.

While building a query plan, the gateway now sees an "unowned type"
as belonging to the parentGroup's service. I'm unsure if this has
any reaching implications (previously an unowned type or field would
throw), however this code path was never exercised in our test cases.
* Clean up artifacts of previous impl
* Begin refactor by preserving serviceNames on types rather than
  keeping only the latest
This commit implements value types as a core part of composition
and query planning. With this work, we now:
* build another metadata map for tracking value types
* attach value type metadata to schema type nodes
* make query planning decisions based on value type metadata
With value types, it doesn't make sense for a since service to own
the fields on the type. Update the types and implementation to reflect
this.

This change surfaced a potential bug, so I added a couple tests to ensure
things work and resolve as expected when @provides directives are used on
fields belonging to a value type.
While the implementation currently works, it depends on a field's
`serviceName` being falsey rather than leveraging the new
`belongsToValueType` on the field's `federation` object.
@trevor-scheer trevor-scheer force-pushed the trevor/value-types-bug branch from 59e5047 to cd3934b Compare August 27, 2019 21:51
@trevor-scheer trevor-scheer merged commit 1852033 into master Aug 27, 2019
@trevor-scheer trevor-scheer deleted the trevor/value-types-bug branch August 27, 2019 23:53
@abernix abernix added this to the Release 2.9.1 milestone Aug 29, 2019
abernix pushed a commit to apollographql/federation that referenced this pull request Sep 4, 2020
…ql/apollo-server#3182)

This commit implements value types as a core part of composition
and query planning. With this work, we now:

* build another metadata map for tracking value types
* attach value type metadata to schema type nodes
* attach value type metadata to fields on value types
* make query planning decisions based on value type metadata

With value types, it doesn't make sense for a single service to own
the fields on said value type. Update the types and implementation to reflect
this.
Apollo-Orig-Commit-AS: apollographql/apollo-server@1852033
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 16, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fragment cannot be spread here as objects of type "_Entity" can never be of type "X"
3 participants