diff --git a/packages/react-relay/__tests__/RelayResolverInterface-test.js b/packages/react-relay/__tests__/RelayResolverInterface-test.js index 342c5c1fed170..61bf5806af0ab 100644 --- a/packages/react-relay/__tests__/RelayResolverInterface-test.js +++ b/packages/react-relay/__tests__/RelayResolverInterface-test.js @@ -263,6 +263,45 @@ describe.each([ }, ); +describe.each([ + { + animalType: 'Octopus', + numLegs: '8', + }, + { + animalType: 'PurpleOctopus', + numLegs: '8', + }, +])( + 'resolvers can return an interface where all implementors are weak model types: %s', + ({animalType, numLegs}) => { + function WeakAnimalLegsQueryComponent(props: {request: {ofType: string}}) { + const data = useClientQuery( + graphql` + query RelayResolverInterfaceTestWeakAnimalLegsQuery( + $request: WeakAnimalRequest! + ) { + weak_animal(request: $request) { + ...RelayResolverInterfaceTestWeakAnimalLegsFragment + } + } + `, + {request: props.request}, + ); + return ; + } + + test(`should read the legs of a ${animalType}`, () => { + const animalRenderer = TestRenderer.create( + + + , + ); + expect(animalRenderer.toJSON()).toEqual(numLegs); + }); + }, +); + test('resolvers can return a list of interfaces where all implementors are strong model types', () => { function AnimalsLegsQueryComponent(props: { requests: Array<{ofType: string, returnValidID: boolean}>, diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalLegsQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalLegsQuery.graphql.js new file mode 100644 index 0000000000000..cc705d80f2f1b --- /dev/null +++ b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalLegsQuery.graphql.js @@ -0,0 +1,222 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<20beace133802bf49b0ab116a3769efa>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ClientRequest, ClientQuery } from 'relay-runtime'; +import type { RelayResolverInterfaceTestWeakAnimalLegsFragment$fragmentType } from "./RelayResolverInterfaceTestWeakAnimalLegsFragment.graphql"; +import {weak_animal as queryWeakAnimalResolverType} from "../../../relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers.js"; +// Type assertion validating that `queryWeakAnimalResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(queryWeakAnimalResolverType: ( + args: {| + request: WeakAnimalRequest, + |}, +) => ?Query__weak_animal$normalization); +import type { Query__weak_animal$normalization } from "./../../../relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animal$normalization.graphql"; +export type WeakAnimalRequest = {| + ofType: string, +|}; +export type RelayResolverInterfaceTestWeakAnimalLegsQuery$variables = {| + request: WeakAnimalRequest, +|}; +export type RelayResolverInterfaceTestWeakAnimalLegsQuery$data = {| + +weak_animal: ?{| + +$fragmentSpreads: RelayResolverInterfaceTestWeakAnimalLegsFragment$fragmentType, + |}, +|}; +export type RelayResolverInterfaceTestWeakAnimalLegsQuery = {| + response: RelayResolverInterfaceTestWeakAnimalLegsQuery$data, + variables: RelayResolverInterfaceTestWeakAnimalLegsQuery$variables, +|}; +*/ + +var node/*: ClientRequest*/ = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "request" + } +], +v1 = [ + { + "kind": "Variable", + "name": "request", + "variableName": "request" + } +], +v2 = [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__relay_model_instance", + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": { + "hasClientEdges": true + }, + "name": "RelayResolverInterfaceTestWeakAnimalLegsQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "concreteType": null, + "modelResolvers": null, + "backingField": { + "alias": null, + "args": (v1/*: any*/), + "fragment": null, + "kind": "RelayResolver", + "name": "weak_animal", + "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers').weak_animal, + "path": "weak_animal", + "normalizationInfo": { + "kind": "OutputType", + "concreteType": null, + "plural": false, + "normalizationNode": require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animal$normalization.graphql') + } + }, + "linkedField": { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "weak_animal", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "RelayResolverInterfaceTestWeakAnimalLegsFragment" + } + ], + "storageKey": null + } + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "RelayResolverInterfaceTestWeakAnimalLegsQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "backingField": { + "name": "weak_animal", + "args": (v1/*: any*/), + "fragment": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + }, + "linkedField": { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "weak_animal", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "name": "legs", + "args": null, + "fragment": { + "kind": "InlineFragment", + "selections": (v2/*: any*/), + "type": "Octopus", + "abstractKey": null + }, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + } + ], + "type": "Octopus", + "abstractKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "name": "legs", + "args": null, + "fragment": { + "kind": "InlineFragment", + "selections": (v2/*: any*/), + "type": "PurpleOctopus", + "abstractKey": null + }, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + } + ], + "type": "PurpleOctopus", + "abstractKey": null + } + ], + "storageKey": null + } + } + ], + "clientAbstractTypes": { + "__isIWeakAnimal": [ + "Octopus", + "PurpleOctopus" + ] + } + }, + "params": { + "cacheID": "a7c1768c48c387258635d29981a0a126", + "id": null, + "metadata": {}, + "name": "RelayResolverInterfaceTestWeakAnimalLegsQuery", + "operationKind": "query", + "text": null + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "618354efaad641b1957a94e6cf22400d"; +} + +module.exports = ((node/*: any*/)/*: ClientQuery< + RelayResolverInterfaceTestWeakAnimalLegsQuery$variables, + RelayResolverInterfaceTestWeakAnimalLegsQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers.js b/packages/relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers.js new file mode 100644 index 0000000000000..235416fe2e4e5 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers.js @@ -0,0 +1,54 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow strict-local + * @oncall relay + */ + +import type {Query__weak_animal$normalization} from 'relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animal$normalization.graphql'; + +/** + * Returns a single `IWeakAnimal` of a given type. + * + * @RelayResolver Query.weak_animal(request: WeakAnimalRequest!): IWeakAnimal + */ +function weak_animal(args: { + request: {ofType: string}, +}): Query__weak_animal$normalization { + switch (args.request.ofType) { + case 'Octopus': + return { + __relay_model_instance: {}, + __typename: 'Octopus', + }; + case 'PurpleOctopus': + return { + __relay_model_instance: {}, + __typename: 'PurpleOctopus', + }; + default: + throw new Error('Invalid type'); + } +} + +/** + * Returns a list of `IWeakAnimal` of a given type. + * + * @RelayResolver Query.weak_animals(requests: [WeakAnimalRequest!]!): [IWeakAnimal] + */ +function weak_animals(args: { + requests: $ReadOnlyArray<{ofType: string}>, +}): Array { + return args.requests.map(request => { + return weak_animal({request}); + }); +} + +module.exports = { + weak_animal, + weak_animals, +}; diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animal$normalization.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animal$normalization.graphql.js new file mode 100644 index 0000000000000..eb088dd198661 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animal$normalization.graphql.js @@ -0,0 +1,77 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<95b83bdc6bd0b02dcaf343e68c8e7782>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { NormalizationSplitOperation } from 'relay-runtime'; + +import type { Octopus } from "../OctopusResolvers.js"; +import type { PurpleOctopus } from "../PurpleOctopusResolvers.js"; +export type Query__weak_animal$normalization = {| + +__typename: "Octopus", + +__relay_model_instance: Octopus, +|} | {| + +__typename: "PurpleOctopus", + +__relay_model_instance: PurpleOctopus, +|}; + +*/ + +var node/*: NormalizationSplitOperation*/ = (function(){ +var v0 = [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__relay_model_instance", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + } +]; +return { + "kind": "SplitOperation", + "metadata": {}, + "name": "Query__weak_animal$normalization", + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "kind": "InlineFragment", + "selections": (v0/*: any*/), + "type": "Octopus", + "abstractKey": null + }, + { + "kind": "InlineFragment", + "selections": (v0/*: any*/), + "type": "PurpleOctopus", + "abstractKey": null + } + ] + } + ] +}; +})(); + +module.exports = node; diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animals$normalization.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animals$normalization.graphql.js new file mode 100644 index 0000000000000..2f36308b1051c --- /dev/null +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animals$normalization.graphql.js @@ -0,0 +1,77 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<782803abb3250f42d9b6fe3b8055e3db>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { NormalizationSplitOperation } from 'relay-runtime'; + +import type { Octopus } from "../OctopusResolvers.js"; +import type { PurpleOctopus } from "../PurpleOctopusResolvers.js"; +export type Query__weak_animals$normalization = {| + +__typename: "Octopus", + +__relay_model_instance: Octopus, +|} | {| + +__typename: "PurpleOctopus", + +__relay_model_instance: PurpleOctopus, +|}; + +*/ + +var node/*: NormalizationSplitOperation*/ = (function(){ +var v0 = [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__relay_model_instance", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + } +]; +return { + "kind": "SplitOperation", + "metadata": {}, + "name": "Query__weak_animals$normalization", + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "kind": "InlineFragment", + "selections": (v0/*: any*/), + "type": "Octopus", + "abstractKey": null + }, + { + "kind": "InlineFragment", + "selections": (v0/*: any*/), + "type": "PurpleOctopus", + "abstractKey": null + } + ] + } + ] +}; +})(); + +module.exports = node; diff --git a/packages/relay-test-utils-internal/schema-extensions/IAnimal.graphql b/packages/relay-test-utils-internal/schema-extensions/IAnimal.graphql index d5e210356e96b..fb97639897c59 100644 --- a/packages/relay-test-utils-internal/schema-extensions/IAnimal.graphql +++ b/packages/relay-test-utils-internal/schema-extensions/IAnimal.graphql @@ -27,3 +27,10 @@ extend type Query { interface IWeakAnimal { legs: Int } + +input WeakAnimalRequest { + """ + Should be a valid GraphQL type that implements `IWeakAnimal`. + """ + ofType: String! +}