Skip to content

Commit

Permalink
feat: support multiple resolvers on fields (#3124)
Browse files Browse the repository at this point in the history
Co-authored-by: Tushar Mathur <[email protected]>
Co-authored-by: Panagiotis <[email protected]>
Co-authored-by: Panagiotis Karatakis <[email protected]>
  • Loading branch information
4 people authored Nov 27, 2024
1 parent d053a88 commit f32ab51
Show file tree
Hide file tree
Showing 52 changed files with 946 additions and 512 deletions.
12 changes: 6 additions & 6 deletions generated/.tailcallrc.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ directive @call(
of the previous step is passed as input to the next step.
"""
steps: [Step]
) on FIELD_DEFINITION | OBJECT
) repeatable on FIELD_DEFINITION | OBJECT

"""
The `@expr` operators allows you to specify an expression that can evaluate to a
value. The expression can be a static value or built form a Mustache template. schema.
"""
directive @expr(
body: JSON
) on FIELD_DEFINITION | OBJECT
) repeatable on FIELD_DEFINITION | OBJECT

"""
The @graphQL operator allows to specify GraphQL API server request to fetch data
Expand Down Expand Up @@ -95,7 +95,7 @@ directive @graphQL(
This refers URL of the API.
"""
url: String!
) on FIELD_DEFINITION | OBJECT
) repeatable on FIELD_DEFINITION | OBJECT

"""
The @grpc operator indicates that a field or node is backed by a gRPC API.For instance,
Expand Down Expand Up @@ -149,7 +149,7 @@ directive @grpc(
This refers to URL of the API.
"""
url: String!
) on FIELD_DEFINITION | OBJECT
) repeatable on FIELD_DEFINITION | OBJECT

"""
The @http operator indicates that a field or node is backed by a REST API.For instance,
Expand Down Expand Up @@ -229,11 +229,11 @@ directive @http(
This refers to URL of the API.
"""
url: String!
) on FIELD_DEFINITION | OBJECT
) repeatable on FIELD_DEFINITION | OBJECT

directive @js(
name: String!
) on FIELD_DEFINITION | OBJECT
) repeatable on FIELD_DEFINITION | OBJECT

"""
The @link directive allows you to import external resources, such as configuration
Expand Down
236 changes: 88 additions & 148 deletions generated/.tailcallrc.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -400,81 +400,13 @@
},
"Field": {
"description": "A field definition containing all the metadata information about resolving a field.",
"type": "object",
"oneOf": [
{
"type": "object",
"required": [
"http"
],
"properties": {
"http": {
"$ref": "#/definitions/Http"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"grpc"
],
"properties": {
"grpc": {
"$ref": "#/definitions/Grpc"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"graphql"
],
"properties": {
"graphql": {
"$ref": "#/definitions/GraphQL"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"call"
],
"properties": {
"call": {
"$ref": "#/definitions/Call"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"js"
],
"properties": {
"js": {
"$ref": "#/definitions/JS"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"expr"
],
"properties": {
"expr": {
"$ref": "#/definitions/Expr"
}
},
"additionalProperties": false
}
"type": [
"object",
"array"
],
"items": {
"$ref": "#/definitions/Resolver"
},
"properties": {
"args": {
"description": "Map of argument name and its definition.",
Expand Down Expand Up @@ -1021,6 +953,82 @@
}
}
},
"Resolver": {
"oneOf": [
{
"type": "object",
"required": [
"http"
],
"properties": {
"http": {
"$ref": "#/definitions/Http"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"grpc"
],
"properties": {
"grpc": {
"$ref": "#/definitions/Grpc"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"graphql"
],
"properties": {
"graphql": {
"$ref": "#/definitions/GraphQL"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"call"
],
"properties": {
"call": {
"$ref": "#/definitions/Call"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"js"
],
"properties": {
"js": {
"$ref": "#/definitions/JS"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"expr"
],
"properties": {
"expr": {
"$ref": "#/definitions/Expr"
}
},
"additionalProperties": false
}
]
},
"RootSchema": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -1336,81 +1344,13 @@
},
"Type": {
"description": "Represents a GraphQL type. A type can be an object, interface, enum or scalar.",
"type": "object",
"oneOf": [
{
"type": "object",
"required": [
"http"
],
"properties": {
"http": {
"$ref": "#/definitions/Http"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"grpc"
],
"properties": {
"grpc": {
"$ref": "#/definitions/Grpc"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"graphql"
],
"properties": {
"graphql": {
"$ref": "#/definitions/GraphQL"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"call"
],
"properties": {
"call": {
"$ref": "#/definitions/Call"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"js"
],
"properties": {
"js": {
"$ref": "#/definitions/JS"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"expr"
],
"properties": {
"expr": {
"$ref": "#/definitions/Expr"
}
},
"additionalProperties": false
}
"type": [
"object",
"array"
],
"items": {
"$ref": "#/definitions/Resolver"
},
"required": [
"fields"
],
Expand Down
2 changes: 1 addition & 1 deletion src/cli/tc/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ async fn confirm_and_write_yml(
fn main_config() -> Config {
let field = Field {
type_of: Type::from("String".to_owned()).into_required(),
resolver: Some(Resolver::Expr(Expr { body: "Hello, World!".into() })),
resolvers: Resolver::Expr(Expr { body: "Hello, World!".into() }).into(),
..Default::default()
};

Expand Down
37 changes: 19 additions & 18 deletions src/core/blueprint/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,19 @@ fn process_field_within_type(
let path_resolver_error_handler = context.path_resolver_error_handler;

if let Some(next_field) = type_info.fields.get(field_name) {
if let Some(resolver) = &next_field.resolver {
return path_resolver_error_handler(
&resolver.directive_name(),
field.type_of.name(),
field_name,
context.original_path,
)
.and(process_path(ProcessPathContext {
if !next_field.resolvers.is_empty() {
let mut valid = Valid::succeed(field.type_of.clone());

for resolver in next_field.resolvers.iter() {
valid = valid.and(path_resolver_error_handler(
&resolver.directive_name(),
field.type_of.name(),
field_name,
context.original_path,
));
}

return valid.and(process_path(ProcessPathContext {
type_info,
is_required,
config_module,
Expand Down Expand Up @@ -464,12 +469,13 @@ fn to_fields(
&add_field.name,
)
.and_then(|field_definition| {
let added_field_path = match source_field.resolver {
Some(_) => add_field.path[1..]
let added_field_path = if source_field.resolvers.is_empty() {
add_field.path.clone()
} else {
add_field.path[1..]
.iter()
.map(|s| s.to_owned())
.collect::<Vec<_>>(),
None => add_field.path.clone(),
.collect::<Vec<_>>()
};
let invalid_path_handler = |field_name: &str,
_added_field_path: &[String],
Expand Down Expand Up @@ -537,13 +543,8 @@ pub fn to_field_definition(
name: &str,
) -> Valid<FieldDefinition, BlueprintError> {
update_args()
.and(update_http().trace(config::Http::trace_name().as_str()))
.and(update_grpc(operation_type).trace(config::Grpc::trace_name().as_str()))
.and(update_const_field().trace(config::Expr::trace_name().as_str()))
.and(update_js_field().trace(config::JS::trace_name().as_str()))
.and(update_graphql(operation_type).trace(config::GraphQL::trace_name().as_str()))
.and(update_resolver(operation_type, object_name))
.and(update_modify().trace(config::Modify::trace_name().as_str()))
.and(update_call(operation_type, object_name).trace(config::Call::trace_name().as_str()))
.and(fix_dangling_resolvers())
.and(update_cache_resolvers())
.and(update_protected(object_name).trace(Protected::trace_name().as_str()))
Expand Down
2 changes: 1 addition & 1 deletion src/core/blueprint/from_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ pub fn to_json_schema(type_of: &Type, config: &Config) -> JsonSchema {
if let Some(type_) = type_ {
let mut schema_fields = BTreeMap::new();
for (name, field) in type_.fields.iter() {
if field.resolver.is_none() {
if field.resolvers.is_empty() {
schema_fields.insert(name.clone(), to_json_schema(&field.type_of, config));
}
}
Expand Down
Loading

1 comment on commit f32ab51

@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 4.22ms 2.18ms 40.09ms 82.62%
Req/Sec 6.13k 0.86k 7.12k 93.50%

731634 requests in 30.02s, 3.67GB read

Requests/sec: 24374.31

Transfer/sec: 125.11MB

Please sign in to comment.