Skip to content

Commit

Permalink
Merge branch 'main' into refact/update-upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
ssddOnTop committed Sep 19, 2024
2 parents 8ce2b24 + 333e1ed commit c0aa708
Show file tree
Hide file tree
Showing 73 changed files with 1,283 additions and 618 deletions.
274 changes: 137 additions & 137 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ path = "src/main.rs"

[workspace.dependencies]
anyhow = "1.0.82"
async-graphql = { version = "7.0.3" }
async-graphql = { version = "7.0.9" }
futures-util = { version = "0.3.30" }
indexmap = "2.2.6"
insta = { version = "1.38.0", features = ["json"] }
Expand Down Expand Up @@ -141,7 +141,7 @@ headers = "0.3.9" # previous version until hyper is updated to 1+
mime = "0.3.17"
htpasswd-verify = { version = "0.3.0", git = "https://github.com/twistedfall/htpasswd-verify", rev = "ff14703083cbd639f7d05622b398926f3e718d61" } # fork version that is wasm compatible
jsonwebtoken = "9.3.0"
async-graphql-value = "7.0.3"
async-graphql-value = "7.0.9"
async-graphql = { workspace = true, features = [
"dynamic-schema",
"dataloader",
Expand Down
4 changes: 4 additions & 0 deletions generated/.tailcallrc.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ directive @link(
"""
id: String
"""
Additional metadata pertaining to the linked resource.
"""
meta: JSON
"""
The source of the link. It can be a URL or a path to a file. If a path is provided,
it is relative to the file that imports the link.
"""
Expand Down
3 changes: 3 additions & 0 deletions generated/.tailcallrc.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,9 @@
"null"
]
},
"meta": {
"description": "Additional metadata pertaining to the linked resource."
},
"src": {
"description": "The source of the link. It can be a URL or a path to a file. If a path is provided, it is relative to the file that imports the link.",
"type": "string"
Expand Down
8 changes: 8 additions & 0 deletions src/core/blueprint/index.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use indexmap::IndexMap;

use super::InputObjectTypeDefinition;
use crate::core::blueprint::{
Blueprint, Definition, FieldDefinition, InputFieldDefinition, SchemaDefinition,
};
Expand Down Expand Up @@ -78,6 +79,13 @@ impl Index {
false
}
}

pub fn get_input_type_definition(&self, type_name: &str) -> Option<&InputObjectTypeDefinition> {
match self.map.get(type_name) {
Some((Definition::InputObject(input), _)) => Some(input),
_ => None,
}
}
}

impl From<&Blueprint> for Index {
Expand Down
3 changes: 3 additions & 0 deletions src/core/config/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,7 @@ pub struct Link {
/// The type of the link. It can be `Config`, or `Protobuf`.
#[serde(default, skip_serializing_if = "is_default", rename = "type")]
pub type_of: LinkType,
/// Additional metadata pertaining to the linked resource.
#[serde(default, skip_serializing_if = "is_default")]
pub meta: Option<serde_json::Value>,
}
1 change: 1 addition & 0 deletions src/core/generator/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ impl Generator {
id: None,
src: metadata.path.to_owned(),
type_of: LinkType::Protobuf,
meta: None,
});
Ok(config)
}
Expand Down
1 change: 1 addition & 0 deletions src/core/grpc/data_loader_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ mod tests {
id: None,
src: test_file.to_string(),
type_of: LinkType::Protobuf,
meta: None,
}]);
let method = GrpcMethod {
package: "greetings".to_string(),
Expand Down
1 change: 1 addition & 0 deletions src/core/grpc/protobuf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ pub mod tests {
id: Some(id.clone()),
src: path.to_string(),
type_of: LinkType::Protobuf,
meta: None,
}]);

let method = GrpcMethod { package: id, service: "a".to_owned(), name: "b".to_owned() };
Expand Down
1 change: 1 addition & 0 deletions src/core/grpc/request_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ mod tests {
id: Some(id.clone()),
src: test_file.to_string(),
type_of: LinkType::Protobuf,
meta: None,
}]);
let method = GrpcMethod {
package: id.to_string(),
Expand Down
42 changes: 40 additions & 2 deletions src/core/http/request_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,13 @@ async fn handle_request_inner<T: DeserializeOwned + GraphQLRequestLike>(

graphql_request::<T>(req, &Arc::new(app_ctx), req_counter).await
}

hyper::Method::GET if req.uri().path() == "/status" => {
let status_response = Response::builder()
.status(StatusCode::OK)
.header(CONTENT_TYPE, "application/json")
.body(Body::from(r#"{"message": "ready"}"#))?;
Ok(status_response)
}
hyper::Method::GET => {
if let Some(TelemetryExporter::Prometheus(prometheus)) =
app_ctx.blueprint.telemetry.export.as_ref()
Expand All @@ -333,7 +339,6 @@ async fn handle_request_inner<T: DeserializeOwned + GraphQLRequestLike>(
return prometheus_metrics(prometheus);
}
};

not_found()
}
_ => not_found(),
Expand Down Expand Up @@ -380,6 +385,39 @@ pub async fn handle_request<T: DeserializeOwned + GraphQLRequestLike>(

#[cfg(test)]
mod test {
use super::*;
use crate::core::async_graphql_hyper::GraphQLRequest;
use crate::core::blueprint::Blueprint;
use crate::core::config::{Config, ConfigModule};
use crate::core::rest::EndpointSet;
use crate::core::runtime::test::init;
use crate::core::valid::Validator;

#[tokio::test]
async fn test_health_endpoint() -> anyhow::Result<()> {
let sdl = tokio::fs::read_to_string(tailcall_fixtures::configs::JSONPLACEHOLDER).await?;
let config = Config::from_sdl(&sdl).to_result()?;
let blueprint = Blueprint::try_from(&ConfigModule::from(config))?;
let app_ctx = Arc::new(AppContext::new(
blueprint,
init(None),
EndpointSet::default(),
));

let req = Request::builder()
.method(Method::GET)
.uri("http://localhost:8000/status".to_string())
.body(Body::empty())?;

let resp = handle_request::<GraphQLRequest>(req, app_ctx).await?;

assert_eq!(resp.status(), StatusCode::OK);
let body = hyper::body::to_bytes(resp.into_body()).await?;
assert_eq!(body, r#"{"message": "ready"}"#);

Ok(())
}

#[test]
fn test_create_allowed_headers() {
use std::collections::BTreeSet;
Expand Down
2 changes: 1 addition & 1 deletion src/core/ir/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ impl IR {
second.eval(ctx).await
}
IR::Discriminate(discriminator, expr) => expr.eval(ctx).await.and_then(|value| {
let value = value.map(|mut value| {
let value = value.map(&mut |mut value| {
let type_name = discriminator.resolve_type(&value)?;

value.set_type_name(type_name.to_string())?;
Expand Down
3 changes: 2 additions & 1 deletion src/core/ir/resolver_context_like.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ impl SelectionField {
let name = field.output_name.to_string();
let type_name = field.type_of.name();
let selection_set = field
.iter_only(|field| match &field.type_condition {
.iter()
.filter(|field| match &field.type_condition {
Some(type_condition) => type_condition == type_name,
None => true,
})
Expand Down
33 changes: 14 additions & 19 deletions src/core/jit/common/jp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use serde::Deserialize;
use crate::core::blueprint::Blueprint;
use crate::core::config::{Config, ConfigModule};
use crate::core::jit::builder::Builder;
use crate::core::jit::store::{Data, Store};
use crate::core::jit::store::Store;
use crate::core::jit::synth::Synth;
use crate::core::jit::{self, OperationPlan, Positioned, Variables};
use crate::core::jit::{OperationPlan, Variables};
use crate::core::json::{JsonLike, JsonObjectLike};
use crate::core::valid::Validator;

Expand All @@ -25,11 +25,9 @@ struct TestData<Value> {
users: Vec<Value>,
}

type Entry<Value> = Data<Result<Value, Positioned<jit::Error>>>;

struct ProcessedTestData<Value> {
posts: Value,
users: HashMap<usize, Entry<Value>>,
users: Value,
}

impl<'a, Value: JsonLike<'a> + Deserialize<'a> + Clone + 'a> TestData<Value> {
Expand All @@ -56,7 +54,7 @@ impl<'a, Value: JsonLike<'a> + Deserialize<'a> + Clone + 'a> TestData<Value> {
map
});

let users: HashMap<_, _> = posts
let users: Vec<_> = posts
.iter()
.map(|post| {
let user_id = post
Expand All @@ -74,12 +72,12 @@ impl<'a, Value: JsonLike<'a> + Deserialize<'a> + Clone + 'a> TestData<Value> {
Value::null()
}
})
.map(Ok)
.map(Data::Single)
.enumerate()
.collect();

ProcessedTestData { posts: Value::array(posts.clone()), users }
ProcessedTestData {
posts: Value::array(posts.clone()),
users: Value::array(users),
}
}
}

Expand Down Expand Up @@ -120,15 +118,12 @@ impl<'a, Value: Deserialize<'a> + Clone + 'a + JsonLike<'a>> JP<Value> {
.id
.to_owned();

let store = [
(posts_id, Data::Single(Ok(posts))),
(users_id, Data::Multiple(users)),
]
.into_iter()
.fold(Store::new(), |mut store, (id, data)| {
store.set_data(id, data);
store
});
let store = [(posts_id, Ok(posts)), (users_id, Ok(users))]
.into_iter()
.fold(Store::new(), |mut store, (id, data)| {
store.set_data(id, data);
store
});

Synth::new(plan, store, vars)
}
Expand Down
19 changes: 11 additions & 8 deletions src/core/jit/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ impl<'a, Input: Clone, Output> Context<'a, Input, Output> {
Self { request, value: None, args: Self::build_args(field), field }
}

pub fn with_value(&self, value: &'a Output) -> Self {
Self {
request: self.request,
// TODO: no need to build again?
args: Self::build_args(self.field),
value: Some(value),
field: self.field,
}
}

pub fn with_value_and_field(
&self,
value: &'a Output,
Expand All @@ -70,16 +80,9 @@ impl<'a, Input: Clone, Output> Context<'a, Input, Output> {

for arg in field.args.iter() {
let name = arg.name.as_str();
let value = arg
.value
.clone()
// TODO: default value resolution should happen in the InputResolver
.or_else(|| arg.default_value.clone());
let value = arg.value.clone();
if let Some(value) = value {
arg_map.insert(Name::new(name), value);
} else if !arg.type_of.is_nullable() {
// TODO: throw error here
todo!()
}
}
Some(arg_map)
Expand Down
5 changes: 5 additions & 0 deletions src/core/jit/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ pub enum BuildError {
pub enum ResolveInputError {
#[error("Variable `{0}` is not defined")]
VariableIsNotFound(String),
#[error("Argument `{arg_name}` for field `{field_name}` is required")]
ArgumentIsRequired {
arg_name: String,
field_name: String,
},
}

#[derive(Error, Debug, Clone)]
Expand Down
Loading

1 comment on commit c0aa708

@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 11.57ms 4.57ms 136.88ms 87.52%
Req/Sec 2.19k 252.29 2.70k 83.83%

261865 requests in 30.03s, 1.31GB read

Requests/sec: 8718.99

Transfer/sec: 44.75MB

Please sign in to comment.