Skip to content

Commit

Permalink
refactor: add MergeRight derive macro (#1723)
Browse files Browse the repository at this point in the history
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Tushar Mathur <[email protected]>
  • Loading branch information
3 people authored Apr 16, 2024
1 parent 69f22a6 commit 4553eec
Show file tree
Hide file tree
Showing 16 changed files with 320 additions and 314 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ async-graphql = { workspace = true, features = [
dotenvy = "0.15"
convert_case = "0.6.0"
rand = "0.8.5"
tailcall-macros = { path = "tailcall-macros" }
tonic-types = "0.11.0"


Expand Down Expand Up @@ -197,6 +198,7 @@ members = [
"tailcall-autogen",
"tailcall-aws-lambda",
"tailcall-cloudflare",
"tailcall-macros",
"tailcall-prettier",
"tailcall-query-plan",
]
Expand Down
124 changes: 39 additions & 85 deletions src/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,21 @@ use crate::directive::DirectiveCodec;
use crate::http::Method;
use crate::is_default;
use crate::json::JsonSchema;
use crate::macros::MergeRight;
use crate::merge_right::MergeRight;
use crate::valid::{Valid, Validator};

#[derive(
Serialize, Deserialize, Clone, Debug, Default, Setters, PartialEq, Eq, schemars::JsonSchema,
Serialize,
Deserialize,
Clone,
Debug,
Default,
Setters,
PartialEq,
Eq,
schemars::JsonSchema,
MergeRight,
)]
#[serde(rename_all = "camelCase")]
pub struct Config {
Expand Down Expand Up @@ -154,28 +164,12 @@ impl Config {
}
}

impl MergeRight for Config {
fn merge_right(self, other: Self) -> Self {
let server = self.server.merge_right(other.server);
let types = merge_types(self.types, other.types);
let unions = merge_unions(self.unions, other.unions);
let schema = self.schema.merge_right(other.schema);
let upstream = self.upstream.merge_right(other.upstream);
let links = merge_links(self.links, other.links);
let telemetry = self.telemetry.merge_right(other.telemetry);

Self { server, upstream, types, schema, unions, links, telemetry }
}
}

fn merge_links(self_links: Vec<Link>, other_links: Vec<Link>) -> Vec<Link> {
self_links.merge_right(other_links)
}

///
/// Represents a GraphQL type.
/// A type can be an object, interface, enum or scalar.
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema)]
#[derive(
Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema, MergeRight,
)]
pub struct Type {
///
/// A map of field name and its definition.
Expand Down Expand Up @@ -229,22 +223,9 @@ impl Type {
}
}

impl MergeRight for Type {
fn merge_right(mut self, other: Self) -> Self {
let fields = self.fields.merge_right(other.fields);
self.implements = self.implements.merge_right(other.implements);
if let Some(ref variants) = self.variants {
if let Some(ref other) = other.variants {
self.variants = Some(variants.union(other).cloned().collect());
}
} else {
self.variants = other.variants;
}
Self { fields, ..self }
}
}

#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize, Eq, schemars::JsonSchema)]
#[derive(
Clone, Debug, Default, PartialEq, Deserialize, Serialize, Eq, schemars::JsonSchema, MergeRight,
)]
#[serde(deny_unknown_fields)]
/// Used to represent an identifier for a type. Typically used via only by the
/// configuration generators to provide additional information about the type.
Expand All @@ -253,7 +234,7 @@ pub struct Tag {
pub id: String,
}

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Eq, schemars::JsonSchema)]
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Eq, schemars::JsonSchema, MergeRight)]
/// The @cache operator enables caching for the query, field or type it is
/// applied to.
#[serde(rename_all = "camelCase")]
Expand All @@ -264,38 +245,22 @@ pub struct Cache {
pub max_age: NonZeroU64,
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Default, schemars::JsonSchema)]
#[derive(
Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Default, schemars::JsonSchema, MergeRight,
)]
pub struct Protected {}

fn merge_types(
mut self_types: BTreeMap<String, Type>,
other_types: BTreeMap<String, Type>,
) -> BTreeMap<String, Type> {
for (name, mut other_type) in other_types {
if let Some(self_type) = self_types.remove(&name) {
other_type = self_type.merge_right(other_type);
}

self_types.insert(name, other_type);
}
self_types
}

fn merge_unions(
mut self_unions: BTreeMap<String, Union>,
other_unions: BTreeMap<String, Union>,
) -> BTreeMap<String, Union> {
for (name, mut other_union) in other_unions {
if let Some(self_union) = self_unions.remove(&name) {
other_union = self_union.merge_right(other_union);
}
self_unions.insert(name, other_union);
}
self_unions
}

#[derive(
Serialize, Deserialize, Clone, Debug, Default, Setters, PartialEq, Eq, schemars::JsonSchema,
Serialize,
Deserialize,
Clone,
Debug,
Default,
Setters,
PartialEq,
Eq,
schemars::JsonSchema,
MergeRight,
)]
#[setters(strip_option)]
pub struct RootSchema {
Expand All @@ -306,17 +271,6 @@ pub struct RootSchema {
pub subscription: Option<String>,
}

impl MergeRight for RootSchema {
// TODO: add unit-tests
fn merge_right(self, other: Self) -> Self {
Self {
query: self.query.merge_right(other.query),
mutation: self.mutation.merge_right(other.mutation),
subscription: self.subscription.merge_right(other.subscription),
}
}
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
/// Used to omit a field from public consumption.
Expand Down Expand Up @@ -409,6 +363,13 @@ pub struct Field {
pub protected: Option<Protected>,
}

// It's a terminal implementation of MergeRight
impl MergeRight for Field {
fn merge_right(self, other: Self) -> Self {
other
}
}

impl Field {
pub fn has_resolver(&self) -> bool {
self.http.is_some()
Expand Down Expand Up @@ -522,19 +483,12 @@ pub struct Arg {
pub default_value: Option<Value>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, schemars::JsonSchema)]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, schemars::JsonSchema, MergeRight)]
pub struct Union {
pub types: BTreeSet<String>,
pub doc: Option<String>,
}

impl MergeRight for Union {
fn merge_right(mut self, other: Self) -> Self {
self.types = self.types.merge_right(other.types);
self
}
}

#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
/// The @http operator indicates that a field or node is backed by a REST API.
Expand Down
34 changes: 3 additions & 31 deletions src/config/config_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ use rustls_pki_types::{CertificateDer, PrivateKeyDer};

use crate::blueprint::GrpcMethod;
use crate::config::Config;
use crate::macros::MergeRight;
use crate::merge_right::MergeRight;
use crate::rest::{EndpointSet, Unchecked};
use crate::scalar;

/// A wrapper on top of Config that contains all the resolved extensions and
/// computed values.
#[derive(Clone, Debug, Default, Setters)]
#[derive(Clone, Debug, Default, Setters, MergeRight)]
pub struct ConfigModule {
pub config: Config,
pub extensions: Extensions,
Expand All @@ -39,7 +40,7 @@ impl<A> Deref for Content<A> {
/// Extensions are meta-information required before we can generate the
/// blueprint. Typically, this information cannot be inferred without performing
/// an IO operation, i.e., reading a file, making an HTTP call, etc.
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, MergeRight)]
pub struct Extensions {
/// Contains the file descriptor sets resolved from the links
pub grpc_file_descriptors: Vec<Content<FileDescriptorSet>>,
Expand Down Expand Up @@ -79,35 +80,6 @@ impl Extensions {
}
}

impl MergeRight for Extensions {
fn merge_right(mut self, mut other: Self) -> Self {
self.grpc_file_descriptors = self
.grpc_file_descriptors
.merge_right(other.grpc_file_descriptors);
self.script = self.script.merge_right(other.script.take());
self.cert = self.cert.merge_right(other.cert);
self.keys = if !other.keys.is_empty() {
other.keys
} else {
self.keys
};
self.endpoint_set = self.endpoint_set.merge_right(other.endpoint_set);
self.htpasswd = self.htpasswd.merge_right(other.htpasswd);
self.jwks = self.jwks.merge_right(other.jwks);
self
}
}

impl MergeRight for ConfigModule {
fn merge_right(mut self, other: Self) -> Self {
self.config = self.config.merge_right(other.config);
self.extensions = self.extensions.merge_right(other.extensions);
self.input_types = self.input_types.merge_right(other.input_types);
self.output_types = self.output_types.merge_right(other.output_types);
self
}
}

impl Deref for ConfigModule {
type Target = Config;
fn deref(&self) -> &Self::Target {
Expand Down
6 changes: 5 additions & 1 deletion src/config/cors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ use serde::{Deserialize, Serialize};

use crate::http::Method;
use crate::is_default;
use crate::macros::MergeRight;
use crate::merge_right::MergeRight;

/// Type to configure Cross-Origin Resource Sharing (CORS) for a server.
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema)]
#[derive(
Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema, MergeRight,
)]
#[serde(rename_all = "camelCase")]
pub struct Cors {
/// Indicates whether the server allows credentials (e.g., cookies,
Expand Down
24 changes: 5 additions & 19 deletions src/config/headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ use serde::{Deserialize, Serialize};
use crate::config::cors::Cors;
use crate::config::KeyValue;
use crate::is_default;
use crate::macros::MergeRight;
use crate::merge_right::MergeRight;

#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema)]
#[derive(
Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema, MergeRight,
)]
#[serde(rename_all = "camelCase")]
pub struct Headers {
#[serde(default, skip_serializing_if = "is_default")]
Expand Down Expand Up @@ -47,21 +51,3 @@ impl Headers {
self.cors.clone()
}
}

pub fn merge_headers(current: Option<Headers>, other: Option<Headers>) -> Option<Headers> {
let mut headers = current.clone();

if let Some(other_headers) = other {
if let Some(mut self_headers) = current.clone() {
self_headers.cache_control = other_headers.cache_control.or(self_headers.cache_control);
self_headers.custom.extend(other_headers.custom);
self_headers.cors = other_headers.cors.or(self_headers.cors);

headers = Some(self_headers);
} else {
headers = Some(other_headers);
}
}

headers
}
Loading

1 comment on commit 4553eec

@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 7.37ms 3.32ms 84.57ms 72.47%
Req/Sec 3.43k 118.02 3.86k 86.58%

409821 requests in 30.01s, 2.05GB read

Requests/sec: 13653.93

Transfer/sec: 70.08MB

Please sign in to comment.