Skip to content

Commit

Permalink
feat(discriminator): improve the ergonomics to identify and resolve u…
Browse files Browse the repository at this point in the history
…nion and interface types (#3000)

Co-authored-by: meskill <[email protected]>
Co-authored-by: Tushar Mathur <[email protected]>
  • Loading branch information
3 people authored Oct 26, 2024
1 parent a889fd8 commit eab8326
Show file tree
Hide file tree
Showing 126 changed files with 1,681 additions and 1,772 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion .cache/index-v5/0d/50/9fa0240aa36806aa13c3b26d4be0f807e53f
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@

f384680608b55c586f4b41eecb43ce8a97a951633d44d839b5bedfc9a1e9afa0 {"key":"GET:https://api.deezer.com/user/2529","integrity":"sha256-7cEa/OBMrNTvAlnFoc6LzTOhbj2BEdSg92Y5o88G0Io=","time":1719748996854,"size":1325,"metadata":null,"raw_metadata":null}
a59788fe402c268993231b56f1174e56dc5ae3d2f26a59a7d5c0550ef131aecc {"key":"GET:https://api.deezer.com/user/2529","integrity":"sha256-riGjfZuazQxaVeS6o8Ahh07jjFB+wEgoRbIfn/FBLCU=","time":1729918929552,"size":1318,"metadata":null,"raw_metadata":null}
2 changes: 0 additions & 2 deletions .cache/index-v5/16/d8/395a7f086b43541bcdd945164e45c8dd387e

This file was deleted.

2 changes: 2 additions & 0 deletions .cache/index-v5/18/aa/398fea47d489e7c87691edde2b0f4fa5f3b4
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

f088c120fc7948480ab24a8dee7b2a891ba434f88ac079648297b3b0a5d757da {"key":"GET:http://jsonplaceholder.typicode.com/photos","integrity":"sha256-yTN0m7xBplwOBeNMFBBHlHpng8ymEKlTuV67XnqzmU8=","time":1729918926505,"size":133192,"metadata":null,"raw_metadata":null}
2 changes: 0 additions & 2 deletions .cache/index-v5/1f/9c/596b9620217a9b2f9de8f237872be0a15fe7

This file was deleted.

2 changes: 0 additions & 2 deletions .cache/index-v5/21/81/847ae6bfeab022d3b7c9a28a9ccaa5b59c65

This file was deleted.

2 changes: 2 additions & 0 deletions .cache/index-v5/30/45/972e74875e2fa855fc12ab452971117dda92
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

6ef5556c3f41b191f1a147831751b37094605e9e999176003106699a3f575b67 {"key":"GET:http://jsonplaceholder.typicode.com/users/1","integrity":"sha256-3LAtJul9HZH1TtK3CxQS68LV42jAA8MRS/8648sfX7k=","time":1729918924922,"size":1399,"metadata":null,"raw_metadata":null}
2 changes: 2 additions & 0 deletions .cache/index-v5/3e/f6/cb54dade99e3f57978e88477b915d0eff694
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

2e9cfec608929d69b2925046b5aaba277d66fada8a22d7dc178520c93965646a {"key":"GET:http://jsonplaceholder.typicode.com/todos","integrity":"sha256-niW7YTq5n8UwtYw3nC7e3ctJf138K/iDlGn7f5jwFGw=","time":1729918926899,"size":5154,"metadata":null,"raw_metadata":null}
2 changes: 1 addition & 1 deletion .cache/index-v5/45/7e/47040ac50b852f1a38fe4479070e56b299b1
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@

7e62555ef97159ff6a499e53b4f01e636d2709d05e51ab673877e0550cb229f8 {"key":"GET:https://api.deezer.com/chart","integrity":"sha256-tFJf2JmW1PaicOFNdPf3CRpU6s07L9oFkdIB5rv8aIE=","time":1719748996498,"size":8483,"metadata":null,"raw_metadata":null}
8f6c1c035d52ff54717776ad82cb71b18403a4dbd90c870da0f039237e81e935 {"key":"GET:https://api.deezer.com/chart","integrity":"sha256-G/8Y4XSNXuS8SqNP6E5Ysj2NgvT9oKXw4+12FYNWY4s=","time":1729918929051,"size":8924,"metadata":null,"raw_metadata":null}
2 changes: 2 additions & 0 deletions .cache/index-v5/48/74/7608a210fc01562ecedac30523d028b39fea
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

30740765bee9d84c3b8849201f2d110f4a9e62baa7f7cfd647d9b642506e959a {"key":"GET:http://jsonplaceholder.typicode.com/comments/1","integrity":"sha256-T+/bHAQSw7Vv7AqjqRu7YENhkjIB/4aZ3ZbQkLsVTEU=","time":1729918925818,"size":1278,"metadata":null,"raw_metadata":null}
2 changes: 1 addition & 1 deletion .cache/index-v5/53/10/a53948d6f8b203986a6e64657958db472bb8
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@

438e58713668bd564551a5d48750a807045c6e5ef90ea9f6d9c1f578fc434176 {"key":"GET:https://api.deezer.com/editorial","integrity":"sha256-Kcj7cNjVEJlTssD+MoypS9dn8wVbX/zDlYY2F5VYQbw=","time":1719748996690,"size":2480,"metadata":null,"raw_metadata":null}
41e73f4aed7bd3f3b0d117119ed0b4c84a9d4dbd95145bf8d6868387254aab47 {"key":"GET:https://api.deezer.com/editorial","integrity":"sha256-VSfCfyoSXsRym8vfwPf1V8s65nHh8Spcd7c+u1HLOzA=","time":1729918929327,"size":2476,"metadata":null,"raw_metadata":null}
2 changes: 0 additions & 2 deletions .cache/index-v5/55/cd/231b35190747296317aef55f24c02777c22d

This file was deleted.

2 changes: 2 additions & 0 deletions .cache/index-v5/55/ea/c729936c292df58fba36c3ab995beb919370
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

4ca1a70bb5223ad95086d9d5d8df9432bc00fd9e469368a8a5515fb6b87457d9 {"key":"GET:http://jsonplaceholder.typicode.com/photos/1","integrity":"sha256-Dp3ZVHTx0q+3iKRejP9Tm4L714YY6ljgCoGDFP+pVTk=","time":1729918926720,"size":1211,"metadata":null,"raw_metadata":null}
2 changes: 2 additions & 0 deletions .cache/index-v5/5a/56/eedeb5e5029bf1a8089268309d1d99b95b82
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

81f4568d53fd0b6c540f928305fc023aed4b5cf0770b1631a43ce349d9ce178a {"key":"GET:http://jsonplaceholder.typicode.com/users","integrity":"sha256-QoEZrIUc0aTSG3fFiFySshqLuxegZ2hTxItaRfFJKuk=","time":1729918924776,"size":2910,"metadata":null,"raw_metadata":null}
2 changes: 2 additions & 0 deletions .cache/index-v5/5f/3b/9bca68df9ad96b96e0279b6cb9c27423df9d
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

5525e3e19d276c5f9490a19d2aea414ff5e432020efe61a971e196bd878b6183 {"key":"GET:http://jsonplaceholder.typicode.com/comments","integrity":"sha256-1ZlFPsRBt+1R+/QwsLVXQCXD0W7FSGFUJA1o8ed6f/0=","time":1729918925646,"size":41831,"metadata":null,"raw_metadata":null}
2 changes: 2 additions & 0 deletions .cache/index-v5/6b/2c/6e37b86a06ca566711c8721446f88a402f16
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

ec5ec64e295520cd8a325e5ab24bd9d229c1bc33555a127b84a05302fcd1d8f7 {"key":"GET:http://jsonplaceholder.typicode.com/todos/1","integrity":"sha256-Gem2y7bSLS0hB0HxpKCYHQFN4hun0Zab1GwGhlVW+k0=","time":1729918927068,"size":1161,"metadata":null,"raw_metadata":null}
2 changes: 2 additions & 0 deletions .cache/index-v5/85/74/0bcf6123606c834080fc9ae6a80fbc7afd7c
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

a862eecb319e2e5360dcc8c3191381f9f9e9d21ca377bfd658921ba7351f4530 {"key":"GET:http://jsonplaceholder.typicode.com/posts","integrity":"sha256-ukqsdn9GgJZcsGyS8049+v5LbqOTL2YoxZOYmE3aUH4=","time":1729918925103,"size":8295,"metadata":null,"raw_metadata":null}
2 changes: 0 additions & 2 deletions .cache/index-v5/94/dd/fa07bf0f2b5729f4078f8fa0c7b0f7f089e8

This file was deleted.

2 changes: 1 addition & 1 deletion .cache/index-v5/a6/44/a2de334c2a9de8fcbeea800160023b12ef42
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@

e3ddfeaefe077fc9fa32abdb8a00239c949dca4db1a7d39ca5e47ad71af2bed3 {"key":"GET:https://api.deezer.com/album/302127","integrity":"sha256-Haxku+8WgcUoGCmd6090+ezPSnOSL3mJ7gRAD4jdHRw=","time":1719748992408,"size":2818,"metadata":null,"raw_metadata":null}
b517c5ac356c6850255b0d1eae8e9c2f54b745855a6835c3d05731bb64e9b2b8 {"key":"GET:https://api.deezer.com/album/302127","integrity":"sha256-fC61r+BgzIizWGMhDPflFgJPzuy+whJGhLFYVTZGGPE=","time":1729918925014,"size":3615,"metadata":null,"raw_metadata":null}
2 changes: 0 additions & 2 deletions .cache/index-v5/a7/83/bea0aee4b8508b7f70f5a7b123d9f4e1447c

This file was deleted.

2 changes: 0 additions & 2 deletions .cache/index-v5/b7/e6/ab1e9ac818818c68cd75a4059a9560ea4e52

This file was deleted.

2 changes: 1 addition & 1 deletion .cache/index-v5/bf/06/5bc0932db3a16ea1d335c4d520f18e2383c5
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@

a691bf6cbf884ca2c03a3e8493df05f6f63cdb0f88cad63518c0ca3ace899e24 {"key":"GET:https://api.deezer.com/playlist/908622995","integrity":"sha256-8rJYugim4jE+qoNSUZG3gGg26DU4xiFIxmH/UZ2+4h0=","time":1719748996098,"size":10620,"metadata":null,"raw_metadata":null}
e677f1d2cb99d03c136ae0bbcb0270591aa2cda218f0415a3672c745ac11d09d {"key":"GET:https://api.deezer.com/playlist/908622995","integrity":"sha256-N/pOfkVlS5lMl31fbVO5h3rAvS2ZaCayr5DRZXVW6iQ=","time":1729918928659,"size":12598,"metadata":null,"raw_metadata":null}
2 changes: 0 additions & 2 deletions .cache/index-v5/c2/f6/ce26c72472a641688383fea297ebb4a67cf5

This file was deleted.

2 changes: 0 additions & 2 deletions .cache/index-v5/c4/91/4e961e5fe13dd4297bde9d57040286322331

This file was deleted.

2 changes: 2 additions & 0 deletions .cache/index-v5/c9/d8/5c58e5e52bd5fa5171267a33b5116be81480
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

5583f421fcb4a663df8193db18a94606535c280fec869e92188482d5eec29d6c {"key":"GET:http://jsonplaceholder.typicode.com/comments?postId=1","integrity":"sha256-WTZWBzJdOdkLP+CVFajHalI/gXaa1JNSAGLLthf9/yY=","time":1729918927233,"size":1765,"metadata":null,"raw_metadata":null}
2 changes: 0 additions & 2 deletions .cache/index-v5/d5/fd/93338e2641ebdf8edc78314395f43b0c50a5

This file was deleted.

2 changes: 1 addition & 1 deletion .cache/index-v5/d9/c0/68d3d1b731fcbc05595f7145224b715df3d6
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@

767ccf322505cb99cf5d0ab638d7d0d256c70ddda61d026e858aa7816ca24bbb {"key":"GET:https://api.deezer.com/track/3135556","integrity":"sha256-KyLgU7OZEr/uZTQY8qWiODefzRG/kt6msu8/yH3M5aU=","time":1719748992121,"size":2349,"metadata":null,"raw_metadata":null}
d58ef53cd1b1aa0afc9c3e1bf84b3e20b5ffc760ca20027cf442672ca3ac69ca {"key":"GET:https://api.deezer.com/track/3135556","integrity":"sha256-8PUN4zq5m2XmdmW456/uscwK7FIMdEO3MtK1ouZjz8U=","time":1729918924756,"size":2761,"metadata":null,"raw_metadata":null}
2 changes: 1 addition & 1 deletion .cache/index-v5/dc/8f/0a1f0bb20ea2ee0f4402570a34c8276b00f7
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@

339af8c81b286fb519eacb32e2329ca584a9e7ecd4a13a9368412019f5fd5d14 {"key":"GET:https://api.deezer.com/search?q=eminem","integrity":"sha256-9QWZcUbVM8x8F7K8Ud9avXDCN5W4+8l6+2ll+vFFnQw=","time":1719748997307,"size":1159,"metadata":null,"raw_metadata":null}
192f0b1251536b1de7ab44f59602bc50721175af12eef643da3436a4bf607ecb {"key":"GET:https://api.deezer.com/search?q=eminem","integrity":"sha256-tfosFMSfh6ejLZf9lu3grDB1aMRdSkUCxflOldP4K6A=","time":1729918930181,"size":1163,"metadata":null,"raw_metadata":null}
2 changes: 0 additions & 2 deletions .cache/index-v5/dd/f4/47e896244d28b8543fc70b70401577a260ad

This file was deleted.

2 changes: 1 addition & 1 deletion .cache/index-v5/e6/57/a828ada010420c50cf18d57762675dab61c6
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@

c672b85180b8cb16ae0fd2b2902a6c4d55f581c7a5156c3bdcdde4b7204946b3 {"key":"GET:https://api.deezer.com/artist/27","integrity":"sha256-DaUvO4VLx1JVy4skdkYG/DRmVNU4v3XWjwx1NfMc4WE=","time":1719748992566,"size":1394,"metadata":null,"raw_metadata":null}
0e037f975b06346f8aac00291998fcb85b90e694e086fbea63c86adf016f2bf9 {"key":"GET:https://api.deezer.com/artist/27","integrity":"sha256-4Cjj+HA93/4EJfhVb103pBja4YMOFoLPo4PtqJSwSLM=","time":1729918925243,"size":1386,"metadata":null,"raw_metadata":null}
2 changes: 2 additions & 0 deletions .cache/index-v5/eb/cf/474593f41b0bd59755eec4d265aae630f8fb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

0c35f4503eb3bbeead927304839254f130d16ac7be5cb843fe7e85d4e44ad498 {"key":"GET:http://jsonplaceholder.typicode.com/posts/1","integrity":"sha256-8Zu2600BElex5J0jr5Bs2TscP0cJICKnwEqGjXAlGMI=","time":1729918924754,"size":1292,"metadata":null,"raw_metadata":null}
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,13 @@ docker run -p 8080:8080 -p 8081:8081 ghcr.io/tailcallhq/tailcall/tc-server
The below file is a standard `.graphQL` file, with a few additions such as `@server` and `@http` directives. So, basically, we specify the GraphQL schema and how to resolve that GraphQL schema in the same file, without having to write any code!

```graphql
schema
@server(port: 8000, hostname: "0.0.0.0")
@upstream(baseURL: "http://jsonplaceholder.typicode.com", httpCache: 42) {
schema @server(port: 8000, hostname: "0.0.0.0") @upstream(httpCache: 42) {
query: Query
}

type Query {
posts: [Post] @http(path: "/posts")
user(id: Int!): User @http(path: "/users/{{.args.id}}")
posts: [Post] @http(url: "http://jsonplaceholder.typicode.com/posts")
user(id: Int!): User @http(url: "http://jsonplaceholder.typicode.com/users/{{.args.id}}")
}

type User {
Expand All @@ -81,7 +79,7 @@ type Post {
userId: Int!
title: String!
body: String!
user: User @http(path: "/users/{{.value.userId}}")
user: User @http(url: "http://jsonplaceholder.typicode.com/users/{{.value.userId}}")
}
```

Expand Down
13 changes: 13 additions & 0 deletions generated/.tailcallrc.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,19 @@ directive @upstream(
verifySSL: Boolean
) on SCHEMA

"""
The `@discriminate` directive is used to drive Tailcall discriminator to use a field
of an object to resolve the type. For example with the directive applied on a field
`@discriminate(field: "object_type")` and the given value `{"foo": "bar", "object_type":
"Buzz"}` the resolved type of the object will be `Buzz`. If `field` is not applied
it defaults to "type". The `field` does not have to be part of the GraphQL Schema,
but it is required to be part of the JSON response. In case this field is missing
from the response an appropriate error message will be displayed.
"""
directive @discriminate(
field: String
) on FIELD_DEFINITION

"""
Empty scalar type represents an empty value.
"""
Expand Down
24 changes: 24 additions & 0 deletions generated/.tailcallrc.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,19 @@
}
}
},
"Discriminate": {
"description": "The `@discriminate` directive is used to drive Tailcall discriminator to use a field of an object to resolve the type. For example with the directive applied on a field `@discriminate(field: \"object_type\")` and the given value `{\"foo\": \"bar\", \"object_type\": \"Buzz\"}` the resolved type of the object will be `Buzz`. If `field` is not applied it defaults to \"type\". The `field` does not have to be part of the GraphQL Schema, but it is required to be part of the JSON response. In case this field is missing from the response an appropriate error message will be displayed.",
"type": "object",
"properties": {
"field": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": false
},
"Email": {
"title": "Email",
"description": "Field whose value conforms to the standard internet email address format as specified in HTML Spec: https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address."
Expand Down Expand Up @@ -491,6 +504,17 @@
"$ref": "#/definitions/Directive"
}
},
"discriminate": {
"description": "Used to overwrite the default discrimination strategy",
"anyOf": [
{
"$ref": "#/definitions/Discriminate"
},
{
"type": "null"
}
]
},
"doc": {
"description": "Publicly visible documentation for the field.",
"type": [
Expand Down
54 changes: 21 additions & 33 deletions src/core/blueprint/interface_resolver.rs
Original file line number Diff line number Diff line change
@@ -1,57 +1,45 @@
use std::collections::HashSet;
use std::collections::BTreeSet;

use crate::core::blueprint::FieldDefinition;
use crate::core::config;
use crate::core::config::{ConfigModule, Field};
use crate::core::config::{ConfigModule, Discriminate, Field, Type};
use crate::core::ir::model::IR;
use crate::core::ir::Discriminator;
use crate::core::try_fold::TryFold;
use crate::core::valid::{Valid, Validator};

fn compile_interface_resolver(
config: &ConfigModule,
interface_name: &str,
interface_types: HashSet<String>,
interface_types: &BTreeSet<String>,
discriminate: &Option<Discriminate>,
) -> Valid<Discriminator, String> {
Valid::from_iter(&interface_types, |type_name| {
Valid::from_option(
config
.find_type(type_name)
.map(|type_| (type_name.as_str(), type_)),
"Can't find a type that is member of interface type".to_string(),
)
})
.and_then(|types| {
let types: Vec<_> = types.into_iter().collect();
let typename_field = discriminate.as_ref().map(|d| d.get_field());

Discriminator::new(interface_name, &types)
})
Discriminator::new(
interface_name.to_string(),
interface_types.clone(),
typename_field,
)
}

pub fn update_interface_resolver<'a>(
) -> TryFold<'a, (&'a ConfigModule, &'a Field, &'a config::Type, &'a str), FieldDefinition, String>
{
TryFold::<(&ConfigModule, &Field, &config::Type, &str), FieldDefinition, String>::new(
) -> TryFold<'a, (&'a ConfigModule, &'a Field, &'a Type, &'a str), FieldDefinition, String> {
TryFold::<(&ConfigModule, &Field, &Type, &str), FieldDefinition, String>::new(
|(config, field, _, _), mut b_field| {
let Some(interface_types) = config.interfaces_types_map().get(field.type_of.name())
else {
return Valid::succeed(b_field);
};

compile_interface_resolver(
config,
field.type_of.name(),
interface_types.iter().cloned().collect(),
)
.map(|discriminator| {
b_field.resolver = Some(
compile_interface_resolver(field.type_of.name(), interface_types, &field.discriminate)
.map(|discriminator| {
b_field.resolver = Some(
b_field
.resolver
.unwrap_or(IR::ContextPath(vec![b_field.name.clone()])),
);
b_field.map_expr(move |expr| IR::Discriminate(discriminator, expr.into()));
b_field
.resolver
.unwrap_or(IR::ContextPath(vec![b_field.name.clone()])),
);
b_field.map_expr(move |expr| IR::Discriminate(discriminator, expr.into()));
b_field
})
})
},
)
}
Loading

1 comment on commit eab8326

@github-actions
Copy link

Choose a reason for hiding this comment

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

Running 30s test @ http://localhost:8000/graphql

4 threads and 100 connections

Thread Stats Avg Stdev Max +/- Stdev
Latency 8.29ms 3.52ms 119.16ms 90.87%
Req/Sec 3.07k 441.83 4.43k 79.08%

366512 requests in 30.02s, 1.84GB read

Requests/sec: 12210.48

Transfer/sec: 62.67MB

Please sign in to comment.