From bbedca3ed1a9cf4849be82d4141fca761cb7cd69 Mon Sep 17 00:00:00 2001 From: David Glasser Date: Tue, 29 Mar 2022 13:49:06 -0700 Subject: [PATCH] tests: stop using undocumented __resolveObject AS feature Apollo Server 2.2.1 introduced an experimental feature named __resolveObject. It was like an extra-powerful version of __resolveReference (which did not yet exist) which also allowed you to return references within a single subgraph and they'd be magically converted into full objects. It was never documented and was described multiple times as experimental. Its implementation is entirely inside the Apollo Server schema instrumentation, which is largely designed for enabling willResolveField plugins (eg, usage reporting). In fact, as the developer of the main feature in AS 3.6.0 (https://github.com/apollographql/apollo-server/pull/5963) I added an optimization to not call schema instrumentation at all if there are no willResolveField plugin hooks... which means that this feature is broken in that case! We are likely to remove this feature in AS4. To get ready for that, stop using it in tests here. This involves changing the existing ones to `__resolveReference` and also adding some direct "object resolution" to resolvers that return objects within the "owning" service. This is an improvement anyway, so that our fixtures use the documented `__resolveReference` feature instead of a different obscure feature. --- .../src/fixtures/accounts.ts | 53 +++++++++---------- .../src/fixtures/books.ts | 4 +- .../__tests__/integration/complex-key.test.ts | 7 +-- 3 files changed, 29 insertions(+), 35 deletions(-) diff --git a/federation-integration-testsuite-js/src/fixtures/accounts.ts b/federation-integration-testsuite-js/src/fixtures/accounts.ts index 572159bac..eb9b763d0 100644 --- a/federation-integration-testsuite-js/src/fixtures/accounts.ts +++ b/federation-integration-testsuite-js/src/fixtures/accounts.ts @@ -6,17 +6,9 @@ export const url = `https://${name}.api.com.invalid`; export const typeDefs = gql` directive @stream on FIELD directive @transform(from: String!) on FIELD - directive @tag(name: String!) repeatable on - | FIELD_DEFINITION - | INTERFACE - | OBJECT - | UNION - | ARGUMENT_DEFINITION - | SCALAR - | ENUM - | ENUM_VALUE - | INPUT_OBJECT - | INPUT_FIELD_DEFINITION + directive @tag( + name: String! + ) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION enum CacheControlScope @tag(name: "from-reviews") { PUBLIC @tag(name: "from-reviews") @@ -29,7 +21,9 @@ export const typeDefs = gql` inheritMaxAge: Boolean ) on FIELD_DEFINITION | OBJECT | INTERFACE | UNION - scalar JSON @tag(name: "from-reviews") @specifiedBy(url: "https://json-spec.dev") + scalar JSON + @tag(name: "from-reviews") + @specifiedBy(url: "https://json-spec.dev") schema { query: RootQuery @@ -57,11 +51,16 @@ export const typeDefs = gql` description: String } - type User @key(fields: "id") @key(fields: "username name { first last }") @tag(name: "from-accounts") { + type User + @key(fields: "id") + @key(fields: "username name { first last }") + @tag(name: "from-accounts") { id: ID! @tag(name: "accounts") name: Name @cacheControl(inheritMaxAge: true) username: String @shareable # Provided by the 'reviews' subgraph - birthDate(locale: String @tag(name: "admin")): String @tag(name: "admin") @tag(name: "dev") + birthDate(locale: String @tag(name: "admin")): String + @tag(name: "admin") + @tag(name: "dev") account: AccountType metadata: [UserMetadata] ssn: String @@ -130,21 +129,21 @@ const libraryUsers: { [name: string]: string[] } = { export const resolvers: GraphQLResolverMap = { RootQuery: { user(_, args) { - return { id: args.id }; + return users.find((user) => user.id === args.id); }, me() { - return { id: '1' }; + return users.find((user) => user.id === '1'); }, }, User: { - __resolveObject(object) { + __resolveReference(object) { // Nested key example for @key(fields: "username name { first last }") if (object.username && object.name.first && object.name.last) { - users.find(user => user.username === object.username); + users.find((user) => user.username === object.username); } - return users.find(user => user.id === object.id); + return users.find((user) => user.id === object.id); }, birthDate(user, args) { return args.locale @@ -154,20 +153,20 @@ export const resolvers: GraphQLResolverMap = { : user.birthDate; }, metadata(object) { - const metaIndex = metadata.findIndex(m => m.id === object.id); - return metadata[metaIndex].metadata.map(obj => ({ name: obj.name })); + const metaIndex = metadata.findIndex((m) => m.id === object.id); + return metadata[metaIndex].metadata.map((obj) => ({ name: obj.name })); }, }, UserMetadata: { address(object) { - const metaIndex = metadata.findIndex(m => - m.metadata.find(o => o.name === object.name), + const metaIndex = metadata.findIndex((m) => + m.metadata.find((o) => o.name === object.name), ); return metadata[metaIndex].metadata[0].address; }, description(object) { - const metaIndex = metadata.findIndex(m => - m.metadata.find(o => o.name === object.name), + const metaIndex = metadata.findIndex((m) => + m.metadata.find((o) => o.name === object.name), ); return metadata[metaIndex].metadata[0].description; }, @@ -177,13 +176,13 @@ export const resolvers: GraphQLResolverMap = { const libraryUserIds = libraryUsers[name]; return libraryUserIds && libraryUserIds.find((id: string) => id === userId) - ? { id: userId } + ? users.find((user) => user.id === userId) : null; }, }, Mutation: { login(_, args) { - return users.find(user => user.username === args.username); + return users.find((user) => user.username === args.username); }, }, }; diff --git a/federation-integration-testsuite-js/src/fixtures/books.ts b/federation-integration-testsuite-js/src/fixtures/books.ts index b14b4a983..7890d1616 100644 --- a/federation-integration-testsuite-js/src/fixtures/books.ts +++ b/federation-integration-testsuite-js/src/fixtures/books.ts @@ -106,7 +106,7 @@ const books = [ export const resolvers: GraphQLResolverMap = { Book: { - __resolveObject(object) { + __resolveReference(object) { return books.find(book => book.isbn === object.isbn); }, similarBooks(object) { @@ -124,7 +124,7 @@ export const resolvers: GraphQLResolverMap = { }, Query: { book(_, args) { - return { isbn: args.isbn }; + return books.find(book => book.isbn === args.isbn); }, books() { return books; diff --git a/gateway-js/src/__tests__/integration/complex-key.test.ts b/gateway-js/src/__tests__/integration/complex-key.test.ts index 08a2eefe7..5b673aacd 100644 --- a/gateway-js/src/__tests__/integration/complex-key.test.ts +++ b/gateway-js/src/__tests__/integration/complex-key.test.ts @@ -91,12 +91,7 @@ const userService: ServiceDefinitionModule = { ); }, organization(user) { - return { id: user.organizationId }; - }, - }, - Organization: { - __resolveObject(object) { - return organizations.find(org => org.id === object.id); + return organizations.find(org => org.id === user.organizationId); }, }, },