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

chore: impl ServiceDoc to blueprint directly #3170

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 16 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ thiserror = "1.0.59"
url = { version = "2.5.0", features = ["serde"] }
convert_case = "0.6.0"
tailcall-valid = "0.1.1"
miette = "7.2.0"
enum_dispatch = "0.3.13"

[dependencies]
# dependencies specific to CLI must have optional = true and the dep should be added to default feature.
Expand Down Expand Up @@ -176,6 +178,8 @@ tailcall-valid = { workspace = true }
dashmap = "6.1.0"
urlencoding = "2.1.3"
tailcall-chunk = "0.2.5"
miette = { workspace = true }
enum_dispatch = { workspace = true }

# to build rquickjs bindings on systems without builtin bindings
[target.'cfg(all(target_os = "windows", target_arch = "x86"))'.dependencies]
Expand Down
49 changes: 49 additions & 0 deletions src/core/blueprint/from_service_document/enum_ty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use async_graphql::parser::types::{EnumType, TypeDefinition};

Check warning on line 1 in src/core/blueprint/from_service_document/enum_ty.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

Diff in /home/runner/work/tailcall/tailcall/src/core/blueprint/from_service_document/enum_ty.rs
use async_graphql::Positioned;
use tailcall_valid::{Valid, Validator};
use crate::core::blueprint;
use crate::core::blueprint::Definition;
use crate::core::blueprint::from_service_document::{Error, helpers, pos_name_to_string};
use crate::core::blueprint::from_service_document::from_service_document::BlueprintMetadata;
use crate::core::config::Alias;
use crate::core::directive::DirectiveCodec;

impl BlueprintMetadata {

Check warning on line 11 in src/core/blueprint/from_service_document/enum_ty.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

Diff in /home/runner/work/tailcall/tailcall/src/core/blueprint/from_service_document/enum_ty.rs
pub(super) fn to_enum_ty(&self, enum_: &EnumType, type_definition: &Positioned<TypeDefinition>) -> Valid<Definition, Error> {
Valid::from_iter(enum_.values.iter(), |value| {
let name = value.node.value.node.as_str().to_owned();
let alias = value
.node
.directives
.iter()
.find(|d| d.node.name.node.as_str() == Alias::directive_name());
let directives = helpers::extract_directives(value.node.directives.iter());

let description = value.node.description.as_ref().map(|d| d.node.to_string());

Check warning on line 22 in src/core/blueprint/from_service_document/enum_ty.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

Diff in /home/runner/work/tailcall/tailcall/src/core/blueprint/from_service_document/enum_ty.rs
directives.and_then(|directives| {
if let Some(alias) = alias {
Alias::from_directive(&alias.node).map(|alias| (directives, name, alias.options, description))
} else {
Valid::succeed((directives, name, Default::default(), description))
}
})

Check warning on line 29 in src/core/blueprint/from_service_document/enum_ty.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

Diff in /home/runner/work/tailcall/tailcall/src/core/blueprint/from_service_document/enum_ty.rs
}).and_then(|variants| {
helpers::extract_directives(type_definition.node.directives.iter()).and_then(|directives| {
let enum_def = blueprint::EnumTypeDefinition {
name: pos_name_to_string(&type_definition.node.name),
directives,
description: type_definition.node.description.as_ref().map(|d| d.node.to_string()),
enum_values: variants.into_iter().map(|(directives, name, alias, description)| {
blueprint::EnumValueDefinition {
name,
directives,
description,
alias,
}
}).collect(),
};
Valid::succeed(Definition::Enum(enum_def))
})
})
}
}
98 changes: 98 additions & 0 deletions src/core/blueprint/from_service_document/from_service_document.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use async_graphql::parser::types::{EnumType, ServiceDocument, TypeDefinition, TypeKind, TypeSystemDefinition, UnionType};

Check warning on line 1 in src/core/blueprint/from_service_document/from_service_document.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

Diff in /home/runner/work/tailcall/tailcall/src/core/blueprint/from_service_document/from_service_document.rs
use async_graphql::Positioned;
use tailcall_valid::{Valid, Validator};

use crate::core::blueprint;

Check warning on line 5 in src/core/blueprint/from_service_document/from_service_document.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

Diff in /home/runner/work/tailcall/tailcall/src/core/blueprint/from_service_document/from_service_document.rs
use crate::core::blueprint::{Blueprint, Definition, ScalarTypeDefinition};
use crate::core::blueprint::from_service_document::{Error, helpers, pos_name_to_string};
use crate::core::config::Alias;
use crate::core::directive::DirectiveCodec;
use crate::core::scalar::Scalar;
use crate::core::try_fold::TryFold;

pub struct BlueprintMetadata {
pub path: String,
}

impl BlueprintMetadata {
pub fn new(path: String) -> Self {
Self { path }
}

pub fn to_blueprint<'a>(&self, doc: ServiceDocument) -> Valid<Blueprint, super::Error> {

Check failure on line 22 in src/core/blueprint/from_service_document/from_service_document.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

this lifetime isn't used in the function definition
let schema = self.to_schema().transform::<Blueprint>(
|schema, blueprint| blueprint.schema(schema),
|blueprint| blueprint.schema,
);

Check warning on line 27 in src/core/blueprint/from_service_document/from_service_document.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

Diff in /home/runner/work/tailcall/tailcall/src/core/blueprint/from_service_document/from_service_document.rs
// Create Definitions with fields and types without resolvers
// and blind conversion to Definition instead of starting from root node(s)
let definitions = self.to_type_defs()
.transform::<Blueprint>(
|defs, blueprint| blueprint.definitions(defs),
|blueprint| blueprint.definitions,
);

schema
.and(definitions)
.try_fold(&doc, Blueprint::default())
}

fn to_schema(&self) -> TryFold<ServiceDocument, blueprint::SchemaDefinition, super::Error> {
TryFold::<ServiceDocument, blueprint::SchemaDefinition, super::Error>::new(|doc, schema| {

Check warning on line 42 in src/core/blueprint/from_service_document/from_service_document.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

Diff in /home/runner/work/tailcall/tailcall/src/core/blueprint/from_service_document/from_service_document.rs
self.schema_definition(doc).and_then(|schema_def| {
self.to_bp_schema_def(doc, &schema_def)
})
})
}
fn to_type_defs(&self) -> TryFold<ServiceDocument, Vec<Definition>, super::Error> {
TryFold::<ServiceDocument, Vec<Definition>, super::Error>::new(|doc, defs| {

Check warning on line 49 in src/core/blueprint/from_service_document/from_service_document.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

Diff in /home/runner/work/tailcall/tailcall/src/core/blueprint/from_service_document/from_service_document.rs
let type_defs = doc.definitions.iter().filter_map(|c| {
match c {
TypeSystemDefinition::Type(ty) => Some(ty),
_ => None,
}
}).collect::<Vec<_>>();
self.populate_defs(type_defs)
.and_then(|defs| self.populate_resolvers(defs))
})
}

fn populate_defs(
&self,
type_defs: Vec<&Positioned<TypeDefinition>>,
) -> Valid<Vec<Definition>, super::Error> {
Valid::from_iter(type_defs.iter(), |type_definition| {
let type_kind = &type_definition.node.kind;
match type_kind {
TypeKind::Scalar => self.to_scalar_ty(type_definition),
TypeKind::Union(union_) => self.to_union_ty(union_, type_definition),
TypeKind::Enum(enum_) => self.to_enum_ty(enum_, type_definition),
TypeKind::Object(obj) => self.to_object_ty(obj, type_definition),
TypeKind::Interface(interface) => self.to_object_ty(interface, type_definition),
TypeKind::InputObject(inp) => self.to_input_object_ty(inp, type_definition),
}
})
}
}

#[cfg(test)]
mod tests {
use tailcall_valid::Validator;

use crate::core::blueprint::from_service_document::from_service_document::BlueprintMetadata;

#[test]
fn test_from_bp() {
// Test code here

Check warning on line 87 in src/core/blueprint/from_service_document/from_service_document.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

Diff in /home/runner/work/tailcall/tailcall/src/core/blueprint/from_service_document/from_service_document.rs
let path = format!("{}/examples/hello.graphql", env!("CARGO_MANIFEST_DIR"));
println!("{}", path);
let doc = async_graphql::parser::parse_schema(std::fs::read_to_string(&path).unwrap()).unwrap();
let bp = BlueprintMetadata::new(path)
.to_blueprint(doc)
.to_result()
.unwrap();

println!("{:#?}", bp.definitions);
}
}
22 changes: 22 additions & 0 deletions src/core/blueprint/from_service_document/helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use std::slice::Iter;
use async_graphql::parser::types::ConstDirective;
use async_graphql::Positioned;
use tailcall_valid::{Valid, ValidationError};
use crate::core::blueprint;
use tailcall_valid::Validator;

pub fn extract_directives(iter: Iter<Positioned<ConstDirective>>) -> Valid<Vec<blueprint::directive::Directive>, super::Error> {
Valid::from_iter(iter, |directive| {
let directives = Valid::from_iter(directive.node.arguments.iter(), |(k, v)| {
let value = v.clone().node.into_json();
let value = value.map_err(|e| ValidationError::new(e.to_string()));
Valid::from(value.map(|value| (k.node.to_string(), value)))
}).map(|arguments| {
blueprint::directive::Directive {
name: directive.node.name.node.to_string(),
arguments: arguments.into_iter().collect(),
}
});
directives
})
}
15 changes: 15 additions & 0 deletions src/core/blueprint/from_service_document/input_object_ty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use async_graphql::parser::types::{InputObjectType, TypeDefinition};
use async_graphql::Positioned;
use tailcall_valid::Valid;
use crate::core::blueprint::Definition;
use crate::core::blueprint::from_service_document::from_service_document::BlueprintMetadata;

impl BlueprintMetadata {
pub(super) fn to_input_object_ty(
&self,
inp: &InputObjectType,
type_definition: &Positioned<TypeDefinition>,
) -> Valid<Definition, super::Error> {
todo!()
}
}
22 changes: 22 additions & 0 deletions src/core/blueprint/from_service_document/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#![allow(unused)]

use async_graphql::Positioned;
use async_graphql_value::Name;

mod from_service_document;
mod schema;
mod helpers;
mod object;
mod union;
mod scalar;
mod enum_ty;
mod input_object_ty;
pub(super) mod resolvers;
mod populate_resolvers;


pub(super) type Error = String;

pub(super) fn pos_name_to_string(pos: &Positioned<Name>) -> String {
pos.node.to_string()
}
83 changes: 83 additions & 0 deletions src/core/blueprint/from_service_document/object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use async_graphql::parser::types::{FieldDefinition, InterfaceType, ObjectType, TypeDefinition};
use async_graphql::Positioned;
use async_graphql_value::Name;
use tailcall_valid::{Valid, Validator};

use crate::core::{blueprint, Type};
use crate::core::blueprint::Definition;
use crate::core::blueprint::from_service_document::{Error, helpers, pos_name_to_string};
use crate::core::blueprint::from_service_document::from_service_document::BlueprintMetadata;

pub(super) trait ObjectLike {
fn fields(&self) -> &Vec<Positioned<FieldDefinition>>;
fn implements(&self) -> &Vec<Positioned<Name>>;
}
impl ObjectLike for ObjectType {
fn fields(&self) -> &Vec<Positioned<FieldDefinition>> {
&self.fields
}
fn implements(&self) -> &Vec<Positioned<Name>> {
&self.implements
}
}
impl ObjectLike for InterfaceType {
fn fields(&self) -> &Vec<Positioned<FieldDefinition>> {
&self.fields
}
fn implements(&self) -> &Vec<Positioned<Name>> {
&self.implements
}
}

impl BlueprintMetadata {
pub(super) fn to_object_ty<Obj: ObjectLike>(
&self,
obj: &Obj,
type_definition: &Positioned<TypeDefinition>,
) -> Valid<Definition, Error> {
Valid::from_iter(obj.fields().iter(), |f| {
let field = &f.node;
let field_name = field.name.node.to_string();
let field_type = &field.ty.node;
let field_args = field.arguments.iter().map(|arg| {
let arg = &arg.node;
let arg_name = arg.name.node.to_string();
let arg_type = &arg.ty.node;
let arg_default = arg.default_value.as_ref().map(|v| v.node.clone().into_json().ok()).flatten();

Check failure on line 46 in src/core/blueprint/from_service_document/object.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

called `map(..).flatten()` on `Option`
blueprint::InputFieldDefinition {
name: arg_name,
of_type: Type::from(arg_type),
default_value: arg_default,
description: arg.description.as_ref().map(|d| d.node.to_string()),
}
}).collect();
let directives = helpers::extract_directives(field.directives.iter());
directives.and_then(|directives| {
let field_type = blueprint::FieldDefinition {
name: field_name,
args: field_args,
directives,
description: field.description.as_ref().map(|d| d.node.to_string()),
resolver: None,
of_type: Type::from(field_type),
default_value: None,
};
Valid::succeed(field_type)
})
}).and_then(|fields| {
helpers::extract_directives(type_definition.node.directives.iter()).and_then(|directives| {
Valid::succeed(
blueprint::ObjectTypeDefinition {
name: pos_name_to_string(&type_definition.node.name),
directives,
description: type_definition.node.description.as_ref().map(|d| d.node.to_string()),
fields,
implements: obj.implements().iter().map(|i| i.node.to_string()).collect(),
}
)
})
}).and_then(|obj| {
Valid::succeed(Definition::Object(obj))
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use tailcall_valid::Valid;
use crate::core::blueprint::Definition;
use crate::core::blueprint::from_service_document::from_service_document::BlueprintMetadata;

impl BlueprintMetadata {
pub(super) fn populate_resolvers(&self, defs: Vec<Definition>) -> Valid<Vec<Definition>, super::Error> {
todo!()
}
}
Empty file.
Loading
Loading