From 66818de5c86febd8dc8a2ec1f2a5b6aa00a8db68 Mon Sep 17 00:00:00 2001 From: Monica Tang Date: Mon, 18 Mar 2024 12:27:34 -0700 Subject: [PATCH] generate type assertion for interface model resolver field Reviewed By: captbaritone Differential Revision: D54604216 fbshipit-source-id: c2c7e0421789e3acb9cb485c3383acb9ae2ec1cc --- ...nterface_of_all_strong_model_type.expected | 8 +- ..._interface_of_all_weak_model_type.expected | 255 ++++++++++++++++++ ..._on_interface_of_all_weak_model_type.input | 44 +++ .../tests/relay_compiler_integration_test.rs | 9 +- compiler/crates/relay-transforms/src/lib.rs | 1 + compiler/crates/relay-typegen/src/visit.rs | 65 +++++ compiler/crates/relay-typegen/src/write.rs | 4 +- ...nterface-of-all-strong-model-type.expected | 43 +++ ...interface-of-all-strong-model-type.graphql | 22 ++ ...-interface-of-all-weak-model-type.expected | 40 +++ ...n-interface-of-all-weak-model-type.graphql | 19 ++ .../relay-typegen/tests/generate_flow/mod.rs | 1 + .../relay-typegen/tests/generate_flow_test.rs | 16 +- 13 files changed, 522 insertions(+), 5 deletions(-) create mode 100644 compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_weak_model_type.expected create mode 100644 compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_weak_model_type.input create mode 100644 compiler/crates/relay-typegen/tests/generate_flow/fixtures/resolver-on-interface-of-all-strong-model-type.expected create mode 100644 compiler/crates/relay-typegen/tests/generate_flow/fixtures/resolver-on-interface-of-all-strong-model-type.graphql create mode 100644 compiler/crates/relay-typegen/tests/generate_flow/fixtures/resolver-on-interface-of-all-weak-model-type.expected create mode 100644 compiler/crates/relay-typegen/tests/generate_flow/fixtures/resolver-on-interface-of-all-weak-model-type.graphql diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type.expected index 0f3b6bf915ce2..0bdf72f4fd5db 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type.expected @@ -162,7 +162,7 @@ module.exports = ((node/*: any*/)/*: Fragment< //- __generated__/PersonComponentFragment.graphql.js /** - * SignedSource<<0d61709f01a89f93e5085f76f5b629a4>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -175,10 +175,14 @@ module.exports = ((node/*: any*/)/*: Fragment< /*:: import type { Fragment, ReaderFragment } from 'relay-runtime'; import type { FragmentType } from "relay-runtime"; +import {Admin as adminRelayModelInstanceResolverType} from "AdminTypeResolvers"; +import {User as userRelayModelInstanceResolverType} from "UserTypeResolvers"; import {name as iPersonNameResolverType} from "IPersonResolvers"; // Type assertion validating that `iPersonNameResolverType` resolver is correctly implemented. // A type error here indicates that the type signature of the resolver module is incorrect. -(iPersonNameResolverType: () => ?string); +(iPersonNameResolverType: ( + model: ReturnType | ReturnType, +) => ?string); declare export opaque type PersonComponentFragment$fragmentType: FragmentType; export type PersonComponentFragment$data = {| +name: ?string, diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_weak_model_type.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_weak_model_type.expected new file mode 100644 index 0000000000000..8001c8f539003 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_weak_model_type.expected @@ -0,0 +1,255 @@ +==================================== INPUT ==================================== +//- PersonComponent.js +graphql`fragment PersonComponentFragment on IPerson { + name +}` + + +//- UserTypeResolvers.js +/** + * @RelayResolver User implements IPerson + * @weak + */ + +//- AdminTypeResolvers.js +/** + * @RelayResolver Admin implements IPerson + * @weak + */ + +//- IPersonResolvers.js +/** + * @RelayResolver IPerson.name: String + */ + +//- relay.config.json +{ + "language": "flow", + "jsModuleFormat": "haste", + "schema": "schema.graphql", + "schemaExtensions": [ + "schema-extensions" + ], + "featureFlags": { + "enable_relay_resolver_transform": true, + "enable_resolver_normalization_ast": true, + "relay_resolver_enable_interface_output_type": { "kind": "enabled" } + } +} + +//- schema.graphql + +//- schema-extensions/extension.graphql +interface IPerson { + id: ID! +} +==================================== OUTPUT =================================== +//- __generated__/Admin____relay_model_instance.graphql.js +/** + * SignedSource<<523d097198aa2ffa2a1209e24ac2a337>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { Admin } from "AdminTypeResolvers"; +import type { FragmentType } from "relay-runtime"; +declare export opaque type Admin____relay_model_instance$fragmentType: FragmentType; +export type Admin____relay_model_instance$data = {| + +__relay_model_instance: Admin, + +$fragmentType: Admin____relay_model_instance$fragmentType, +|}; +export type Admin____relay_model_instance$key = { + +$data?: Admin____relay_model_instance$data, + +$fragmentSpreads: Admin____relay_model_instance$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "Admin____relay_model_instance", + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__relay_model_instance", + "storageKey": null + } + ] + } + ], + "type": "Admin", + "abstractKey": null +}; + +module.exports = ((node/*: any*/)/*: Fragment< + Admin____relay_model_instance$fragmentType, + Admin____relay_model_instance$data, +>*/); + +//- __generated__/PersonComponentFragment.graphql.js +/** + * SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +import {Admin as adminRelayModelInstanceResolverType} from "AdminTypeResolvers"; +import {User as userRelayModelInstanceResolverType} from "UserTypeResolvers"; +import {name as iPersonNameResolverType} from "IPersonResolvers"; +// Type assertion validating that `iPersonNameResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(iPersonNameResolverType: ( + model: ReturnType | ReturnType, +) => ?string); +declare export opaque type PersonComponentFragment$fragmentType: FragmentType; +export type PersonComponentFragment$data = {| + +name: ?string, + +$fragmentType: PersonComponentFragment$fragmentType, +|}; +export type PersonComponentFragment$key = { + +$data?: PersonComponentFragment$data, + +$fragmentSpreads: PersonComponentFragment$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "PersonComponentFragment", + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "Admin____relay_model_instance" + }, + "kind": "RelayResolver", + "name": "name", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('IPersonResolvers').name, '__relay_model_instance', true), + "path": "name" + } + ], + "type": "Admin", + "abstractKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "User____relay_model_instance" + }, + "kind": "RelayResolver", + "name": "name", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('IPersonResolvers').name, '__relay_model_instance', true), + "path": "name" + } + ], + "type": "User", + "abstractKey": null + } + ] + } + ], + "type": "IPerson", + "abstractKey": "__isIPerson" +}; + +(node/*: any*/).hash = "a57dd30bd59412781e9566e1553e2d70"; + +module.exports = ((node/*: any*/)/*: Fragment< + PersonComponentFragment$fragmentType, + PersonComponentFragment$data, +>*/); + +//- __generated__/User____relay_model_instance.graphql.js +/** + * SignedSource<<9a188c26688bb46f65ed80df4ae938c3>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { User } from "UserTypeResolvers"; +import type { FragmentType } from "relay-runtime"; +declare export opaque type User____relay_model_instance$fragmentType: FragmentType; +export type User____relay_model_instance$data = {| + +__relay_model_instance: User, + +$fragmentType: User____relay_model_instance$fragmentType, +|}; +export type User____relay_model_instance$key = { + +$data?: User____relay_model_instance$data, + +$fragmentSpreads: User____relay_model_instance$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "User____relay_model_instance", + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__relay_model_instance", + "storageKey": null + } + ] + } + ], + "type": "User", + "abstractKey": null +}; + +module.exports = ((node/*: any*/)/*: Fragment< + User____relay_model_instance$fragmentType, + User____relay_model_instance$data, +>*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_weak_model_type.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_weak_model_type.input new file mode 100644 index 0000000000000..335423b11079b --- /dev/null +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_weak_model_type.input @@ -0,0 +1,44 @@ +//- PersonComponent.js +graphql`fragment PersonComponentFragment on IPerson { + name +}` + + +//- UserTypeResolvers.js +/** + * @RelayResolver User implements IPerson + * @weak + */ + +//- AdminTypeResolvers.js +/** + * @RelayResolver Admin implements IPerson + * @weak + */ + +//- IPersonResolvers.js +/** + * @RelayResolver IPerson.name: String + */ + +//- relay.config.json +{ + "language": "flow", + "jsModuleFormat": "haste", + "schema": "schema.graphql", + "schemaExtensions": [ + "schema-extensions" + ], + "featureFlags": { + "enable_relay_resolver_transform": true, + "enable_resolver_normalization_ast": true, + "relay_resolver_enable_interface_output_type": { "kind": "enabled" } + } +} + +//- schema.graphql + +//- schema-extensions/extension.graphql +interface IPerson { + id: ID! +} diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration_test.rs b/compiler/crates/relay-compiler/tests/relay_compiler_integration_test.rs index 17de41b237dfc..ce9adf6f293c8 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration_test.rs +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<7bc74fd259b90f455e2dbd166114cc72>> */ mod relay_compiler_integration; @@ -110,6 +110,13 @@ async fn resolver_on_interface_of_all_strong_model_type() { test_fixture(transform_fixture, file!(), "resolver_on_interface_of_all_strong_model_type.input", "relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type.expected", input, expected).await; } +#[tokio::test] +async fn resolver_on_interface_of_all_weak_model_type() { + let input = include_str!("relay_compiler_integration/fixtures/resolver_on_interface_of_all_weak_model_type.input"); + let expected = include_str!("relay_compiler_integration/fixtures/resolver_on_interface_of_all_weak_model_type.expected"); + test_fixture(transform_fixture, file!(), "resolver_on_interface_of_all_weak_model_type.input", "relay_compiler_integration/fixtures/resolver_on_interface_of_all_weak_model_type.expected", input, expected).await; +} + #[tokio::test] async fn resolver_returns_interface_of_all_live_model_type() { let input = include_str!("relay_compiler_integration/fixtures/resolver_returns_interface_of_all_live_model_type.input"); diff --git a/compiler/crates/relay-transforms/src/lib.rs b/compiler/crates/relay-transforms/src/lib.rs index ce4ae608fceff..11439ace403f8 100644 --- a/compiler/crates/relay-transforms/src/lib.rs +++ b/compiler/crates/relay-transforms/src/lib.rs @@ -168,6 +168,7 @@ pub use relay_directive::RelayDirective; pub use relay_node_identifier::RelayLocationAgnosticBehavior; pub use relay_resolvers::get_resolver_fragment_dependency_name; pub use relay_resolvers::relay_resolvers; +pub use relay_resolvers::resolver_type_import_alias; pub use relay_resolvers::FragmentDataInjectionMode; pub use relay_resolvers::RelayResolverMetadata; pub use relay_resolvers::ResolverOutputTypeInfo; diff --git a/compiler/crates/relay-typegen/src/visit.rs b/compiler/crates/relay-typegen/src/visit.rs index 977e2296d8ce9..ba124d96de95f 100644 --- a/compiler/crates/relay-typegen/src/visit.rs +++ b/compiler/crates/relay-typegen/src/visit.rs @@ -17,6 +17,7 @@ use common::ArgumentName; use common::DirectiveName; use common::NamedItem; use docblock_shared::KEY_RESOLVER_ID_FIELD; +use docblock_shared::RELAY_RESOLVER_MODEL_INSTANCE_FIELD; use docblock_shared::RESOLVER_VALUE_SCALAR_NAME; use graphql_ir::Condition; use graphql_ir::Directive; @@ -30,12 +31,14 @@ use graphql_ir::Selection; use indexmap::map::Entry; use indexmap::IndexMap; use indexmap::IndexSet; +use itertools::Itertools; use relay_config::CustomScalarType; use relay_config::CustomScalarTypeImport; use relay_config::TypegenLanguage; use relay_schema::CUSTOM_SCALAR_DIRECTIVE_NAME; use relay_schema::EXPORT_NAME_CUSTOM_SCALAR_ARGUMENT_NAME; use relay_schema::PATH_CUSTOM_SCALAR_ARGUMENT_NAME; +use relay_transforms::resolver_type_import_alias; use relay_transforms::ClientEdgeMetadata; use relay_transforms::FragmentAliasMetadata; use relay_transforms::FragmentDataInjectionMode; @@ -311,6 +314,7 @@ fn generate_resolver_type( resolver_function_name: StringKey, fragment_name: Option, resolver_metadata: &RelayResolverMetadata, + imported_resolvers: &mut ImportedResolvers, ) -> AST { let schema_field = resolver_metadata.field(typegen_context.schema); @@ -323,6 +327,7 @@ fn generate_resolver_type( encountered_enums, custom_scalars, schema_field, + imported_resolvers, ); let inner_ast = match &resolver_metadata.output_type_info { @@ -393,8 +398,67 @@ fn get_resolver_arguments( encountered_enums: &mut EncounteredEnums, custom_scalars: &mut std::collections::HashSet<(StringKey, PathBuf)>, schema_field: &Field, + imported_resolvers: &mut ImportedResolvers, ) -> Vec { let mut resolver_arguments = vec![]; + if let Some(Type::Interface(interface_id)) = schema_field.parent_type { + let interface = typegen_context.schema.interface(interface_id); + let implementing_objects = + interface.recursively_implementing_objects(typegen_context.schema); + for object_id in implementing_objects.iter().sorted() { + let type_name_with_location = typegen_context.schema.object(*object_id).name; + let type_name = type_name_with_location.item.0; + let import_alias = + resolver_type_import_alias(type_name, *RELAY_RESOLVER_MODEL_INSTANCE_FIELD); + let model_import_path = &PathBuf::from( + type_name_with_location + .location + .source_location() + .path() + .intern() + .lookup(), + ); + let model_name = ImportedResolverName::Named { + name: type_name, + import_as: import_alias, + }; + let import_path = typegen_context.project_config.js_module_import_identifier( + &typegen_context + .project_config + .artifact_path_for_definition(typegen_context.definition_source_location), + model_import_path, + ); + let imported_model = ImportedResolver { + resolver_name: model_name, + resolver_type: AST::ReturnTypeOfFunctionWithName(type_name), + import_path, + }; + imported_resolvers + .0 + .entry(type_name) + .or_insert(imported_model); + } + let object_names = SortedASTList::new( + implementing_objects + .iter() + .map(|object_id| { + let object = typegen_context.schema.object(*object_id); + let import_alias = resolver_type_import_alias( + object.name.item.0, + *RELAY_RESOLVER_MODEL_INSTANCE_FIELD, + ); + AST::ReturnTypeOfFunctionWithName(import_alias) + }) + .collect(), + ); + let interface_union_type = AST::Union(object_names); + resolver_arguments.push(KeyValuePairProp { + key: "model".intern(), + optional: false, + read_only: false, + value: interface_union_type, + }); + } if let Some(fragment_name) = fragment_name { if let Some((fragment_name, injection_mode)) = resolver_metadata.fragment_data_injection_mode @@ -501,6 +565,7 @@ fn import_relay_resolver_function_type( local_resolver_name, fragment_name, resolver_metadata, + imported_resolvers, ), import_path, }; diff --git a/compiler/crates/relay-typegen/src/write.rs b/compiler/crates/relay-typegen/src/write.rs index f876082057988..dfb1fe1525d1c 100644 --- a/compiler/crates/relay-typegen/src/write.rs +++ b/compiler/crates/relay-typegen/src/write.rs @@ -569,7 +569,9 @@ fn write_relay_resolver_imports( )?; } } - writer.write(&resolver.resolver_type)?; + if let AST::AssertFunctionType(_) = &resolver.resolver_type { + writer.write(&resolver.resolver_type)?; + } } Ok(()) } diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/resolver-on-interface-of-all-strong-model-type.expected b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/resolver-on-interface-of-all-strong-model-type.expected new file mode 100644 index 0000000000000..4355a89a1a2e2 --- /dev/null +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/resolver-on-interface-of-all-strong-model-type.expected @@ -0,0 +1,43 @@ +==================================== INPUT ==================================== +fragment resolverOnInterfaceOfAllStrongModelTypeFragment on Cat { + description +} + +# %extensions% + +interface Cat { + id: ID! + description: String @relay_resolver(import_path: "CatResolver") +} + +type Tabby implements Cat { + id: ID! + description: String + __relay_model_instance: RelayResolverValue @relay_resolver(fragment_name: "Tabby__id", import_path: "TabbyResolver", inject_fragment_data: "id") +} + +type Persian implements Cat { + id: ID! + description: String + __relay_model_instance: RelayResolverValue @relay_resolver(fragment_name: "Persian__id", import_path: "PersianResolver", inject_fragment_data: "id") +} +==================================== OUTPUT =================================== +import type { FragmentType } from "relay-runtime"; +import {Persian as persianRelayModelInstanceResolverType} from ""; +import {Tabby as tabbyRelayModelInstanceResolverType} from ""; +import catDescriptionResolverType from "CatResolver"; +// Type assertion validating that `catDescriptionResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(catDescriptionResolverType: ( + model: ReturnType | ReturnType, +) => ?mixed); +declare export opaque type resolverOnInterfaceOfAllStrongModelTypeFragment$fragmentType: FragmentType; +export type resolverOnInterfaceOfAllStrongModelTypeFragment$data = {| + +description: ?ReturnType, + +$fragmentType: resolverOnInterfaceOfAllStrongModelTypeFragment$fragmentType, +|}; +export type resolverOnInterfaceOfAllStrongModelTypeFragment$key = { + +$data?: resolverOnInterfaceOfAllStrongModelTypeFragment$data, + +$fragmentSpreads: resolverOnInterfaceOfAllStrongModelTypeFragment$fragmentType, + ... +}; diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/resolver-on-interface-of-all-strong-model-type.graphql b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/resolver-on-interface-of-all-strong-model-type.graphql new file mode 100644 index 0000000000000..6be155ae471ea --- /dev/null +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/resolver-on-interface-of-all-strong-model-type.graphql @@ -0,0 +1,22 @@ +fragment resolverOnInterfaceOfAllStrongModelTypeFragment on Cat { + description +} + +# %extensions% + +interface Cat { + id: ID! + description: String @relay_resolver(import_path: "CatResolver") +} + +type Tabby implements Cat { + id: ID! + description: String + __relay_model_instance: RelayResolverValue @relay_resolver(fragment_name: "Tabby__id", import_path: "TabbyResolver", inject_fragment_data: "id") +} + +type Persian implements Cat { + id: ID! + description: String + __relay_model_instance: RelayResolverValue @relay_resolver(fragment_name: "Persian__id", import_path: "PersianResolver", inject_fragment_data: "id") +} diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/resolver-on-interface-of-all-weak-model-type.expected b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/resolver-on-interface-of-all-weak-model-type.expected new file mode 100644 index 0000000000000..93df23dbd496b --- /dev/null +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/resolver-on-interface-of-all-weak-model-type.expected @@ -0,0 +1,40 @@ +==================================== INPUT ==================================== +fragment resolverOnInterfaceOfAllWeakModelTypeFragment on Cat { + description +} + +# %extensions% + +interface Cat { + description: String @relay_resolver(import_path: "CatResolver") +} + +type Tabby implements Cat { + description: String + __relay_model_instance: RelayResolverValue @relay_resolver(fragment_name: "Tabby__id", import_path: "TabbyResolver", inject_fragment_data: "id") +} + +type Persian implements Cat { + description: String + __relay_model_instance: RelayResolverValue @relay_resolver(fragment_name: "Persian__id", import_path: "PersianResolver", inject_fragment_data: "id") +} +==================================== OUTPUT =================================== +import type { FragmentType } from "relay-runtime"; +import {Persian as persianRelayModelInstanceResolverType} from ""; +import {Tabby as tabbyRelayModelInstanceResolverType} from ""; +import catDescriptionResolverType from "CatResolver"; +// Type assertion validating that `catDescriptionResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(catDescriptionResolverType: ( + model: ReturnType | ReturnType, +) => ?mixed); +declare export opaque type resolverOnInterfaceOfAllWeakModelTypeFragment$fragmentType: FragmentType; +export type resolverOnInterfaceOfAllWeakModelTypeFragment$data = {| + +description: ?ReturnType, + +$fragmentType: resolverOnInterfaceOfAllWeakModelTypeFragment$fragmentType, +|}; +export type resolverOnInterfaceOfAllWeakModelTypeFragment$key = { + +$data?: resolverOnInterfaceOfAllWeakModelTypeFragment$data, + +$fragmentSpreads: resolverOnInterfaceOfAllWeakModelTypeFragment$fragmentType, + ... +}; diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/resolver-on-interface-of-all-weak-model-type.graphql b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/resolver-on-interface-of-all-weak-model-type.graphql new file mode 100644 index 0000000000000..2afeafd3e4902 --- /dev/null +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/resolver-on-interface-of-all-weak-model-type.graphql @@ -0,0 +1,19 @@ +fragment resolverOnInterfaceOfAllWeakModelTypeFragment on Cat { + description +} + +# %extensions% + +interface Cat { + description: String @relay_resolver(import_path: "CatResolver") +} + +type Tabby implements Cat { + description: String + __relay_model_instance: RelayResolverValue @relay_resolver(fragment_name: "Tabby__id", import_path: "TabbyResolver", inject_fragment_data: "id") +} + +type Persian implements Cat { + description: String + __relay_model_instance: RelayResolverValue @relay_resolver(fragment_name: "Persian__id", import_path: "PersianResolver", inject_fragment_data: "id") +} diff --git a/compiler/crates/relay-typegen/tests/generate_flow/mod.rs b/compiler/crates/relay-typegen/tests/generate_flow/mod.rs index 5f0b7d132b87d..f68603ab598d7 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow/mod.rs +++ b/compiler/crates/relay-typegen/tests/generate_flow/mod.rs @@ -64,6 +64,7 @@ pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result .collect(), }, enable_relay_resolver_transform: true, + relay_resolver_enable_interface_output_type: FeatureFlag::Enabled, actor_change_support: FeatureFlag::Enabled, enable_fragment_aliases: FeatureFlag::Enabled, ..Default::default() diff --git a/compiler/crates/relay-typegen/tests/generate_flow_test.rs b/compiler/crates/relay-typegen/tests/generate_flow_test.rs index b9d7b430e0cae..e9581e6e834e2 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow_test.rs +++ b/compiler/crates/relay-typegen/tests/generate_flow_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<0785113b958651724c9f7105d4f66b9a>> + * @generated SignedSource<> */ mod generate_flow; @@ -677,6 +677,20 @@ async fn required_within_aliased_inline_fragment_on_abstract() { test_fixture(transform_fixture, file!(), "required-within-aliased-inline-fragment-on-abstract.graphql", "generate_flow/fixtures/required-within-aliased-inline-fragment-on-abstract.expected", input, expected).await; } +#[tokio::test] +async fn resolver_on_interface_of_all_strong_model_type() { + let input = include_str!("generate_flow/fixtures/resolver-on-interface-of-all-strong-model-type.graphql"); + let expected = include_str!("generate_flow/fixtures/resolver-on-interface-of-all-strong-model-type.expected"); + test_fixture(transform_fixture, file!(), "resolver-on-interface-of-all-strong-model-type.graphql", "generate_flow/fixtures/resolver-on-interface-of-all-strong-model-type.expected", input, expected).await; +} + +#[tokio::test] +async fn resolver_on_interface_of_all_weak_model_type() { + let input = include_str!("generate_flow/fixtures/resolver-on-interface-of-all-weak-model-type.graphql"); + let expected = include_str!("generate_flow/fixtures/resolver-on-interface-of-all-weak-model-type.expected"); + test_fixture(transform_fixture, file!(), "resolver-on-interface-of-all-weak-model-type.graphql", "generate_flow/fixtures/resolver-on-interface-of-all-weak-model-type.expected", input, expected).await; +} + #[tokio::test] async fn roots() { let input = include_str!("generate_flow/fixtures/roots.graphql");