Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support multiple resolvers on fields #3124

Merged
merged 21 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
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(),

Check warning on line 90 in src/cli/tc/init.rs

View check run for this annotation

Codecov / codecov/patch

src/cli/tc/init.rs#L90

Added line #L90 was not covered by tests
..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 @@ -107,14 +107,19 @@ fn process_field_within_type(context: ProcessFieldWithinTypeContext) -> Valid<Ty
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 @@ -424,12 +429,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 @@ -499,13 +505,8 @@ pub fn to_field_definition(
name: &str,
) -> Valid<FieldDefinition, String> {
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
Loading