Skip to content

Commit

Permalink
Support fragment spreads on abstract types defined in client schema e…
Browse files Browse the repository at this point in the history
…xtensions

Reviewed By: alunyov

Differential Revision: D37944141

fbshipit-source-id: 6149ecc78a523768139000fa040b31b279b5900d
  • Loading branch information
captbaritone authored and facebook-github-bot committed Jul 21, 2022
1 parent 4264743 commit a121b33
Show file tree
Hide file tree
Showing 36 changed files with 1,712 additions and 3 deletions.
43 changes: 41 additions & 2 deletions compiler/crates/relay-codegen/src/build_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ use relay_transforms::generate_abstract_type_refinement_key;
use relay_transforms::remove_directive;
use relay_transforms::ClientEdgeMetadata;
use relay_transforms::ClientEdgeMetadataDirective;
use relay_transforms::ClientExtensionAbstractTypeMetadataDirective;
use relay_transforms::ConnectionConstants;
use relay_transforms::ConnectionMetadata;
use relay_transforms::DeferDirective;
Expand Down Expand Up @@ -254,16 +255,54 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
let argument_definitions =
self.build_operation_variable_definitions(&operation.variable_definitions);
let selections = self.build_selections(&mut context, operation.selections.iter());
self.object(object! {
let mut fields = object! {
argument_definitions: Primitive::Key(argument_definitions),
kind: Primitive::String(CODEGEN_CONSTANTS.operation_value),
name: Primitive::String(operation.name.item),
selections: selections,
})
};
if let Some(client_abstract_types) =
self.maybe_build_client_abstract_types(operation)
{
fields.push(client_abstract_types);
}
self.object(fields)
}
}
}

fn maybe_build_client_abstract_types(
&mut self,
operation: &OperationDefinition,
) -> Option<ObjectEntry> {
// If the query contains frament spreads on abstract types which are
// defined in the client schema, we attach extra metadata so that we
// know which concrete types match these type conditions at runtime.
ClientExtensionAbstractTypeMetadataDirective::find(&operation.directives).map(|directive| {
let entries = directive
.abstract_types
.iter()
.map(|abstract_type| {
let concrete_types = self.array(
abstract_type
.concrete
.iter()
.map(|concrete| Primitive::String(*concrete))
.collect(),
);
ObjectEntry {
key: abstract_type.name,
value: Primitive::Key(concrete_types),
}
})
.collect();
ObjectEntry {
key: CODEGEN_CONSTANTS.client_abstract_types,
value: Primitive::Key(self.object(entries)),
}
})
}

pub(crate) fn build_fragment(
&mut self,
fragment: &FragmentDefinition,
Expand Down
2 changes: 2 additions & 0 deletions compiler/crates/relay-codegen/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub struct CodegenConstants {
pub argument_definitions: StringKey,
pub backward: StringKey,
pub cache_id: StringKey,
pub client_abstract_types: StringKey,
pub client_component: StringKey,
pub client_edge_backing_field_key: StringKey,
pub client_edge_to_client_object: StringKey,
Expand Down Expand Up @@ -123,6 +124,7 @@ lazy_static! {
argument_definitions: "argumentDefinitions".intern(),
backward: "backward".intern(),
cache_id: "cacheID".intern(),
client_abstract_types: "clientAbstractTypes".intern(),
client_component: "ClientComponent".intern(),
client_edge_to_server_object: "ClientEdgeToServerObject".intern(),
client_edge_to_client_object: "ClientEdgeToClientObject".intern(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
==================================== INPUT ====================================
query Foo {
client_type {
...MyFragment
}
}

fragment MyFragment on ClientNamed {
name
}

# %extensions%

extend type Query {
client_type: ClientType
}

type ClientType implements ClientNamed {
name: String
}

interface ClientNamed {
name: String
}
==================================== OUTPUT ===================================
{
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "MyFragment",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
}
],
"type": "ClientNamed",
"abstractKey": "__isClientNamed"
}

{
"argumentDefinitions": [],
"kind": "Operation",
"name": "Foo",
"selections": [
{
"alias": null,
"args": null,
"concreteType": "ClientType",
"kind": "LinkedField",
"name": "client_type",
"plural": false,
"selections": [
{
"args": null,
"kind": "FragmentSpread",
"name": "MyFragment"
}
],
"storageKey": null
}
],
"clientAbstractTypes": {
"__isClientNamed": [
"ClientType"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
query Foo {
client_type {
...MyFragment
}
}

fragment MyFragment on ClientNamed {
name
}

# %extensions%

extend type Query {
client_type: ClientType
}

type ClientType implements ClientNamed {
name: String
}

interface ClientNamed {
name: String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
==================================== INPUT ====================================
query Foo {
...QueryFragment
}

fragment QueryFragment on Query {
client_type {
...MyFragment
}
}

fragment MyFragment on ClientNamed {
name
}

# %extensions%

extend type Query {
client_type: ClientType
}

type ClientType implements ClientNamed {
name: String
}

interface ClientNamed {
name: String
}
==================================== OUTPUT ===================================
{
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "MyFragment",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
}
],
"type": "ClientNamed",
"abstractKey": "__isClientNamed"
}

{
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "QueryFragment",
"selections": [
{
"alias": null,
"args": null,
"concreteType": "ClientType",
"kind": "LinkedField",
"name": "client_type",
"plural": false,
"selections": [
{
"args": null,
"kind": "FragmentSpread",
"name": "MyFragment"
}
],
"storageKey": null
}
],
"type": "Query",
"abstractKey": null
}

{
"argumentDefinitions": [],
"kind": "Operation",
"name": "Foo",
"selections": [
{
"args": null,
"kind": "FragmentSpread",
"name": "QueryFragment"
}
],
"clientAbstractTypes": {
"__isClientNamed": [
"ClientType"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
query Foo {
...QueryFragment
}

fragment QueryFragment on Query {
client_type {
...MyFragment
}
}

fragment MyFragment on ClientNamed {
name
}

# %extensions%

extend type Query {
client_type: ClientType
}

type ClientType implements ClientNamed {
name: String
}

interface ClientNamed {
name: String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
==================================== INPUT ====================================
query Foo {
client_type {
...MyFragment
}
}

fragment MyFragment on ClientUnion {
__typename
}

# %extensions%

extend type Query {
client_type: ClientType
}

type ClientType {
name: String
}

type OtherClientType {
name: String
}

union ClientUnion = ClientType | OtherClientType
==================================== OUTPUT ===================================
{
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "MyFragment",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
}
],
"type": "ClientUnion",
"abstractKey": "__isClientUnion"
}

{
"argumentDefinitions": [],
"kind": "Operation",
"name": "Foo",
"selections": [
{
"alias": null,
"args": null,
"concreteType": "ClientType",
"kind": "LinkedField",
"name": "client_type",
"plural": false,
"selections": [
{
"args": null,
"kind": "FragmentSpread",
"name": "MyFragment"
}
],
"storageKey": null
}
],
"clientAbstractTypes": {
"__isClientUnion": [
"ClientType",
"OtherClientType"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
query Foo {
client_type {
...MyFragment
}
}

fragment MyFragment on ClientUnion {
__typename
}

# %extensions%

extend type Query {
client_type: ClientType
}

type ClientType {
name: String
}

type OtherClientType {
name: String
}

union ClientUnion = ClientType | OtherClientType

0 comments on commit a121b33

Please sign in to comment.