Skip to content

Commit

Permalink
fix(config): representation for nested list (#2747)
Browse files Browse the repository at this point in the history
Co-authored-by: Tushar Mathur <[email protected]>
  • Loading branch information
meskill and tusharmath authored Aug 29, 2024
1 parent 7ed198c commit 5dab91b
Show file tree
Hide file tree
Showing 88 changed files with 1,810 additions and 938 deletions.
175 changes: 175 additions & 0 deletions core/blueprint/wrapping_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
use std::fmt::Formatter;
use std::ops::Deref;

use async_graphql::parser::types as async_graphql_types;
use async_graphql::Name;
use serde::{Deserialize, Serialize};

use crate::core::is_default;

/// Type to represent GraphQL type usage with modifiers
/// [spec](https://spec.graphql.org/October2021/#sec-Wrapping-Types)
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)]
#[serde(untagged)]
pub enum Type {
Named {
/// Name of the type
name: String,
/// Flag to indicate the type is required.
#[serde(rename = "required", default, skip_serializing_if = "is_default")]
non_null: bool,
},
List {
/// Type is a list
#[serde(rename = "list")]
of_type: Box<Type>,
/// Flag to indicate the type is required.
#[serde(rename = "required", default, skip_serializing_if = "is_default")]
non_null: bool,
},
}

impl std::fmt::Debug for Type {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Type::Named { name, non_null } => {
if *non_null {
write!(f, "{}!", name)
} else {
write!(f, "{}", name)
}
}
Type::List { of_type, non_null } => {
if *non_null {
write!(f, "[{:?}]!", of_type)
} else {
write!(f, "[{:?}]", of_type)
}
}
}
}
}

impl Default for Type {
fn default() -> Self {
Type::Named { name: "JSON".to_string(), non_null: false }
}
}

impl Type {
/// gets the name of the type
pub fn name(&self) -> &String {
match self {
Type::Named { name, .. } => name,
Type::List { of_type, .. } => of_type.name(),
}
}

/// checks if the type is nullable
pub fn is_nullable(&self) -> bool {
!match self {
Type::Named { non_null, .. } => *non_null,
Type::List { non_null, .. } => *non_null,
}
}
/// checks if the type is a list
pub fn is_list(&self) -> bool {
matches!(self, Type::List { .. })
}

/// convert this type into NonNull type
pub fn into_required(self) -> Self {
match self {
Type::Named { name, .. } => Self::Named { name, non_null: true },
Type::List { of_type, .. } => Self::List { of_type, non_null: true },
}
}

/// convert this into nullable type
pub fn into_nullable(self) -> Self {
match self {
Type::Named { name, .. } => Self::Named { name, non_null: false },
Type::List { of_type, .. } => Self::List { of_type, non_null: false },
}
}

/// create a nullable list type from this type
pub fn into_list(self) -> Self {
Type::List { of_type: Box::new(self), non_null: false }
}

/// convert this type from list to non-list for any level of nesting
pub fn into_single(self) -> Self {
match self {
Type::Named { .. } => self,
Type::List { of_type, .. } => of_type.into_single(),
}
}

/// replace the name of the underlying type
pub fn with_name(self, name: String) -> Self {
match self {
Type::Named { non_null, .. } => Type::Named { name, non_null },
Type::List { of_type, non_null } => {
Type::List { of_type: Box::new(of_type.with_name(name)), non_null }
}
}
}
}

impl From<&async_graphql_types::Type> for Type {
fn from(value: &async_graphql_types::Type) -> Self {
let non_null = !value.nullable;

match &value.base {
async_graphql_types::BaseType::Named(name) => {
Self::Named { name: name.to_string(), non_null }
}
async_graphql_types::BaseType::List(type_) => {
Self::List { of_type: Box::new(type_.as_ref().into()), non_null }
}
}
}
}

impl From<&Type> for async_graphql_types::Type {
fn from(value: &Type) -> Self {
let nullable = value.is_nullable();

let base = match value {
Type::Named { name, .. } => async_graphql_types::BaseType::Named(Name::new(name)),
Type::List { of_type, .. } => async_graphql_types::BaseType::List(Box::new(
async_graphql_types::Type::from(of_type.deref()),
)),
};

async_graphql_types::Type { base, nullable }
}
}

impl From<&Type> for async_graphql::dynamic::TypeRef {
fn from(value: &Type) -> Self {
let nullable = value.is_nullable();

let base = match value {
Type::Named { name, .. } => {
async_graphql::dynamic::TypeRef::Named(name.to_owned().into())
}
Type::List { of_type, .. } => async_graphql::dynamic::TypeRef::List(Box::new(
async_graphql::dynamic::TypeRef::from(of_type.deref()),
)),
};

if nullable {
base
} else {
async_graphql::dynamic::TypeRef::NonNull(Box::new(base))
}
}
}

impl From<String> for Type {
fn from(value: String) -> Self {
Self::Named { name: value, non_null: false }
}
}
77 changes: 53 additions & 24 deletions examples/jsonplaceholder.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,44 +15,61 @@
"Post": {
"fields": {
"body": {
"type": "String",
"required": true
"type": {
"name": "String",
"required": true
}
},
"id": {
"type": "Int",
"required": true
"type": {
"name": "Int",
"required": true
}
},
"title": {
"type": "String",
"required": true
"type": {
"name": "String",
"required": true
}
},
"user": {
"type": "User",
"type": {
"name": "User"
},
"http": {
"path": "/users/{{value.userId}}"
}
},
"userId": {
"type": "Int",
"required": true
"type": {
"name": "Int",
"required": true
}
}
}
},
"Query": {
"fields": {
"posts": {
"type": "Post",
"list": true,
"type": {
"list": {
"name": "Post"
}
},
"http": {
"path": "/posts"
}
},
"user": {
"type": "User",
"type": {
"name": "User"
},
"args": {
"id": {
"type": "Int",
"required": true
"type": {
"name": "Int",
"required": true
}
}
},
"http": {
Expand All @@ -64,26 +81,38 @@
"User": {
"fields": {
"email": {
"type": "String",
"required": true
"type": {
"name": "String",
"required": true
}
},
"id": {
"type": "Int",
"required": true
"type": {
"name": "Int",
"required": true
}
},
"name": {
"type": "String",
"required": true
"type": {
"name": "String",
"required": true
}
},
"phone": {
"type": "String"
"type": {
"name": "String"
}
},
"username": {
"type": "String",
"required": true
"type": {
"name": "String",
"required": true
}
},
"website": {
"type": "String"
"type": {
"name": "String"
}
}
}
}
Expand Down
62 changes: 38 additions & 24 deletions examples/jsonplaceholder.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,51 +10,65 @@ types:
Post:
fields:
body:
type: String
required: true
type:
name: String
required: true
id:
type: Int
required: true
type:
name: Int
required: true
title:
type: String
required: true
type:
name: String
required: true
user:
type: User
type:
name: User
http:
path: /users/{{value.userId}}
userId:
type: Int
required: true
type:
name: Int
required: true
Query:
fields:
posts:
type: Post
list: true
type:
list:
name: Post
http:
path: /posts
user:
type: User
type:
name: User
args:
id:
type: Int
required: true
type:
name: Int
required: true
http:
path: /users/{{args.id}}
User:
fields:
email:
type: String
required: true
type:
name: String
required: true
id:
type: Int
required: true
type:
name: Int
required: true
name:
type: String
required: true
type:
name: String
required: true
phone:
type: String
type:
name: String
username:
type: String
required: true
type:
name: String
required: true
website:
type: String
type:
name: String
Loading

2 comments on commit 5dab91b

@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 269 tests
test run_execution_spec::add-field-index-list.md ... ok
test run_execution_spec::add-field-many-list.md ... ok
test run_execution_spec::add-field-many.md ... ok
test run_execution_spec::add-field-modify.md ... ok
test run_execution_spec::add-field-with-modify.md ... ok
test run_execution_spec::add-field-with-composition.md ... ok
test run_execution_spec::add-field.md ... ok
test run_execution_spec::apollo-tracing.md ... ok
test run_execution_spec::async-cache-disabled.md ... ok
test run_execution_spec::async-cache-enable-multiple-resolvers.md ... ok
test run_execution_spec::async-cache-global.md ... ok
test run_execution_spec::async-cache-enabled.md ... ok
test run_execution_spec::async-cache-inflight-request.md ... ok
test run_execution_spec::auth-protected-without-auth.md ... ok
test run_execution_spec::auth-jwt.md ... ok
test run_execution_spec::auth-basic.md ... ok
test run_execution_spec::batching-disabled.md ... ok
test run_execution_spec::auth.md ... ok
test run_execution_spec::batching-default.md ... ok
test run_execution_spec::batching-group-by-default.md ... ok
test run_execution_spec::batching-group-by-optional-key.md ... ok
test run_execution_spec::batching-group-by.md ... ok
test run_execution_spec::batching-post.md ... ok
test run_execution_spec::batching.md ... ok
test run_execution_spec::cache-control.md ... ok
test run_execution_spec::caching.md ... ok
test run_execution_spec::call-graphql-datasource.md ... ok
test run_execution_spec::caching-collision.md ... ok
test run_execution_spec::call-multiple-steps-piping.md ... ok
test run_execution_spec::call-mutation.md ... ok
test run_execution_spec::call-operator.md ... ok
test run_execution_spec::cors-allow-cred-true.md ... ok
test run_execution_spec::cors-invalid-expose-headers.md ... ok
test run_execution_spec::cors-invalid-headers.md ... ok
test run_execution_spec::cors-allow-cred-false.md ... ok
test run_execution_spec::cors-invalid-methods.md ... ok
test run_execution_spec::cors-invalid-origins.md ... ok
test run_execution_spec::cors-allow-cred-vary.md ... ok
test run_execution_spec::custom-headers.md ... ok
test run_execution_spec::dedupe_batch_query_execution.md ... ok
test run_execution_spec::default-value-arg.md ... ok
test run_execution_spec::experimental-headers-error.md ... ok
test run_execution_spec::default-value-config.md ... ok
test run_execution_spec::env-value.md ... ok
test run_execution_spec::graphql-conformance-002.md ... ok
test run_execution_spec::experimental-headers.md ... ok
test run_execution_spec::graphql-conformance-004.md ... ok
test run_execution_spec::graphql-conformance-005.md ... ok
test run_execution_spec::graphql-conformance-006.md ... ok
test run_execution_spec::graphql-conformance-007.md ... ok
test run_execution_spec::graphql-conformance-008.md ... ok
test run_execution_spec::graphql-conformance-009.md ... ok
test run_execution_spec::graphql-conformance-001.md ... ok
test run_execution_spec::graphql-conformance-011.md ... ok
test run_execution_spec::graphql-conformance-012.md ... ok
test run_execution_spec::graphql-conformance-003.md ... ok
test run_execution_spec::graphql-conformance-010.md ... ok
test run_execution_spec::graphql-conformance-013.md ... FAILED
test run_execution_spec::graphql-conformance-014.md ... ok
test run_execution_spec::graphql-conformance-017.md ... ok
test run_execution_spec::graphql-conformance-015.md ... ok
test run_execution_spec::graphql-conformance-016.md ... ok
test run_execution_spec::graphql-conformance-http-001.md ... ok
test run_execution_spec::graphql-conformance-http-002.md ... ok
test run_execution_spec::graphql-conformance-http-003.md ... ok
test run_execution_spec::graphql-conformance-http-004.md ... ok
test run_execution_spec::graphql-conformance-http-007.md ... ok
test run_execution_spec::graphql-conformance-http-008.md ... ok
test run_execution_spec::graphql-conformance-http-009.md ... ok
test run_execution_spec::graphql-conformance-http-005.md ... ok
test run_execution_spec::graphql-conformance-http-011.md ... ok
test run_execution_spec::graphql-conformance-http-010.md ... ok
test run_execution_spec::graphql-conformance-http-006.md ... ok
test run_execution_spec::graphql-conformance-http-012.md ... ok
test run_execution_spec::graphql-conformance-http-014.md ... ok
test run_execution_spec::graphql-conformance-http-013.md ... FAILED
test run_execution_spec::graphql-conformance-http-017.md ... ok
test run_execution_spec::graphql-conformance-http-016.md ... ok
test run_execution_spec::graphql-conformance-http-015.md ... ok
test run_execution_spec::graphql-dataloader-batch-request.md ... ok
test run_execution_spec::graphql-datasource-errors.md ... ok
test run_execution_spec::graphql-dataloader-no-batch-request.md ... ok
test run_execution_spec::graphql-datasource-mutation.md ... ok
test run_execution_spec::graphql-datasource-no-args.md ... ok
test run_execution_spec::graphql-datasource-query-directives.md ... ok
test run_execution_spec::graphql-datasource-with-args.md ... ok
test run_execution_spec::graphql-datasource-with-mandatory-enum.md ... ok
test run_execution_spec::graphql-datasource-with-empty-enum.md ... ok
test run_execution_spec::graphql-nested-datasource.md ... ok
test run_execution_spec::grpc-batch.md ... ok
test run_execution_spec::grpc-error.md ... ok
test run_execution_spec::grpc-json.md ... ok
test run_execution_spec::grpc-map.md ... ok
test run_execution_spec::grpc-oneof.md ... ok
test run_execution_spec::grpc-override-url-from-upstream.md ... ok
test run_execution_spec::grpc-proto-with-same-package.md ... ok
test run_execution_spec::grpc-reflection.md ... ok
test run_execution_spec::grpc-simple.md ... ok
test run_execution_spec::https.md ... ok
test run_execution_spec::grpc-url-from-upstream.md ... ok
test run_execution_spec::inline-field.md ... ok
test run_execution_spec::inline-index-list.md ... ok
test run_execution_spec::input-type-protected-error.md ... ok
test run_execution_spec::inline-many-list.md ... ok
test run_execution_spec::io-cache.md ... ok
test run_execution_spec::inline-many.md ... ok
test run_execution_spec::js-directive.md ... ok
test run_execution_spec::modified-field.md ... ok
test run_execution_spec::mutation-put.md ... ok
test run_execution_spec::jsonplaceholder-call-post.md ... ok
test run_execution_spec::mutation.md ... ok
test run_execution_spec::n-plus-one-list.md ... ok
test run_execution_spec::n-plus-one.md ... ok
test run_execution_spec::nested-objects.md ... ok
test run_execution_spec::nested-recursive-types.md ... ok
test run_execution_spec::nesting-level3.md ... ok
test run_execution_spec::nullable-arg-query.md ... ok
test run_execution_spec::omit-index-list.md ... ok
test run_execution_spec::predefined-scalar.md ... ok
test run_execution_spec::omit-resolved-by-parent.md ... ok
test run_execution_spec::recursive-types-no-resolver.md ... ok
test run_execution_spec::recursive-types-json.md ... ok
test run_execution_spec::omit-many.md ... ok
test run_execution_spec::recursive-types.md ... ok
test run_execution_spec::ref-other-nested.md ... ok
test run_execution_spec::ref-other.md ... ok
test run_execution_spec::related-fields-recursive.md ... ok
test run_execution_spec::rename-field.md ... ok
test run_execution_spec::resolve-with-headers.md ... ok
test run_execution_spec::request-to-upstream-batching.md ... ok
test run_execution_spec::resolve-with-vars.md ... ok
test run_execution_spec::resolved-by-parent.md ... ok
test run_execution_spec::rest-api-error.md ... ok
test run_execution_spec::rest-api-post.md ... ok
test run_execution_spec::rest-api.md ... ok
test run_execution_spec::showcase.md ... ok
test run_execution_spec::test-add-field-error.md ... ok
test run_execution_spec::simple-graphql.md ... ok
test run_execution_spec::simple-query.md ... ok
test run_execution_spec::test-add-field-list.md ... ok
test run_execution_spec::test-all-blueprint-errors.md ... ok
test run_execution_spec::test-batch-operator-post.md ... ok
test run_execution_spec::test-add-field.md ... ok
test run_execution_spec::test-add-link-to-empty-config.md ... ok
test run_execution_spec::test-call-operator-errors.md ... ok
test run_execution_spec::test-conflict-allowed-headers.md ... ok
test run_execution_spec::test-conflict-vars.md ... ok
test run_execution_spec::test-batching-group-by.md ... ok
test run_execution_spec::test-cache.md ... ok
test run_execution_spec::test-dbl-usage-many.md ... ok
test run_execution_spec::test-custom-scalar.md ... ok
test run_execution_spec::test-directives-undef-null-fields.md ... ok
test run_execution_spec::test-duplicated-link.md ... ok
test run_execution_spec::test-empty-link.md ... ok
test run_execution_spec::test-custom-types.md ... ok
test run_execution_spec::test-enable-jit.md ... ok
test run_execution_spec::test-description-many.md ... ok
test run_execution_spec::test-enum-default.md ... ok
test run_execution_spec::test-enum-empty.md ... ok
test run_execution_spec::test-enum-aliases.md ... ok
test run_execution_spec::test-enum-merge.md ... ok
test run_execution_spec::test-expr-error.md ... ok
test run_execution_spec::test-enum-description.md ... ok
test run_execution_spec::test-expr-with-add-field.md ... ok
test run_execution_spec::test-expr-with-inline.md ... ok
test run_execution_spec::test-expr-scalar-as-string.md ... ok
test run_execution_spec::test-enum.md ... ok
test run_execution_spec::test-field-already-implemented-from-Interface.md ... ok
test run_execution_spec::test-graphql-with-add-field.md ... ok
test run_execution_spec::test-graphqlsource-no-base-url.md ... ok
test run_execution_spec::test-expr-with-mustache.md ... ok
test run_execution_spec::test-groupby-without-batching.md ... ok
test run_execution_spec::test-grpc-group-by.md ... ok
test run_execution_spec::test-grpc-invalid-method-format.md ... ok
test run_execution_spec::test-grpc-invalid-proto-id.md ... ok
test run_execution_spec::test-grpc-missing-fields.md ... ok
test run_execution_spec::test-grpc-nested-data.md ... ok
test run_execution_spec::test-grpc-nested-optional.md ... ok
test run_execution_spec::test-grpc-optional.md ... ok
test run_execution_spec::test-grpc-proto-path.md ... ok
test run_execution_spec::test-grpc-service-method.md ... ok
test run_execution_spec::test-grpc-service.md ... ok
test run_execution_spec::test-expr.md ... ok
test run_execution_spec::test-hostname-faliure.md ... ok
test run_execution_spec::test-graphqlsource.md ... ok
test run_execution_spec::test-grpc.md ... ok
test run_execution_spec::test-http-baseurl.md ... ok
test run_execution_spec::test-http-batchKey.md ... ok
test run_execution_spec::test-http-with-add-field.md ... ok
test run_execution_spec::test-http-with-inline.md ... ok
test run_execution_spec::test-http-headers.md ... ok
test run_execution_spec::test-http-with-mustache-expr.md ... ok
test run_execution_spec::test-inline-error.md ... ok
test run_execution_spec::test-http-tmpl.md ... ok
test run_execution_spec::test-http.md ... ok
test run_execution_spec::test-inline-list.md ... ok
test run_execution_spec::test-inline.md ... ok
test run_execution_spec::test-input-out.md ... ok
test run_execution_spec::test-input-documentation.md ... ok
test run_execution_spec::test-input-with-arg-out.md ... ok
test run_execution_spec::test-interface-from-json.md ... ok
test run_execution_spec::test-invalid-query-in-http.md ... ok
test run_execution_spec::test-invalid-server.md ... ok
test run_execution_spec::test-js-multi-onRequest-handlers.md ... ok
test run_execution_spec::test-js-multiple-scripts.md ... ok
test run_execution_spec::test-interface-result.md ... ok
test run_execution_spec::test-interface.md ... ok
test run_execution_spec::test-lack-resolver.md ... ok
test run_execution_spec::test-js-request-response-2.md ... ok
test run_execution_spec::test-js-request-response.md ... ok
test run_execution_spec::test-list-args.md ... ok
test run_execution_spec::test-merge-batch.md ... ok
test run_execution_spec::test-merge-nested.md ... ok
test run_execution_spec::test-merge-query.md ... ok
test run_execution_spec::test-merge-union.md ... ok
test run_execution_spec::test-merge-right-with-link-config.md ... ok
test run_execution_spec::test-missing-mutation-resolver.md ... ok
test run_execution_spec::test-missing-argument-on-all-resolvers.md ... ok
test run_execution_spec::test-missing-query-resolver.md ... ok
test run_execution_spec::test-missing-root-types.md ... ok
test run_execution_spec::test-missing-schema-query.md ... ok
test run_execution_spec::test-merge-server-sdl.md ... ok
test run_execution_spec::test-multiple-config-types.md ... ok
test run_execution_spec::test-multiple-resolvable-directives-on-field.md ... ok
test run_execution_spec::test-multi-interface.md ... ok
test run_execution_spec::test-modify.md ... ok
test run_execution_spec::test-nested-input.md ... ok
test run_execution_spec::test-no-base-url.md ... ok
test run_execution_spec::test-nested-value.md ... ok
test run_execution_spec::test-nested-link.md ... ok
test run_execution_spec::test-null-in-array.md ... ok
test run_execution_spec::test-null-in-object.md ... ok
test run_execution_spec::test-optional-key-skip-empty.md ... ok
test run_execution_spec::test-omit-list.md ... ok
test run_execution_spec::test-omit.md ... ok
test run_execution_spec::test-params-as-body.md ... ok
test run_execution_spec::test-query-documentation.md ... ok
test run_execution_spec::test-query.md ... ok
test run_execution_spec::test-ref-other.md ... ok
test run_execution_spec::test-response-header-value.md ... ok
test run_execution_spec::test-response-headers-multi.md ... ok
test run_execution_spec::test-response-header-merge.md ... ok
test run_execution_spec::test-response-headers-name.md ... ok
test run_execution_spec::test-required-fields.md ... FAILED
test run_execution_spec::test-scalars-builtin.md ... ok
test run_execution_spec::test-scalars-integers.md ... ok
test run_execution_spec::test-scalars-validation.md ... ok
test run_execution_spec::test-server-base-types.md ... ok
test run_execution_spec::test-set-cookie-headers.md ... ok
test run_execution_spec::test-server-vars.md ... ok
test run_execution_spec::test-undefined-query.md ... ok
test run_execution_spec::test-static-value.md ... ok
test run_execution_spec::test-union-many-types.md ... ok
test run_execution_spec::test-union-same-types.md ... ok
test run_execution_spec::test-union-ambiguous.md ... ok
test run_execution_spec::test-scalars.md ... ok
test run_execution_spec::test-upstream-headers.md ... ok
test run_execution_spec::undeclared-type-no-base-url.md ... ok
test run_execution_spec::undeclared-type.md ... ok
test run_execution_spec::test-union.md ... ok
test run_execution_spec::upstream-batching.md ... ok
test run_execution_spec::test-upstream.md ... ok
test run_execution_spec::upstream-fail-request.md ... ok
test run_execution_spec::with-args-url.md ... ok
test run_execution_spec::with-nesting.md ... ok
test run_execution_spec::with-args.md ... ok
test run_execution_spec::yaml-nested-unions.md ... ok
test run_execution_spec::yaml-union.md ... ok
test run_execution_spec::yaml-union-in-type.md ... ok

failures:

---- run_execution_spec::graphql-conformance-013.md ----
test panicked: snapshot assertion for 'graphql-conformance-013.md_0' failed in line 202

---- run_execution_spec::graphql-conformance-http-013.md ----
test panicked: snapshot assertion for 'graphql-conformance-http-013.md_0' failed in line 202

---- run_execution_spec::test-required-fields.md ----
test panicked: snapshot assertion for 'test-required-fields.md_8' failed in line 202

failures:
run_execution_spec::graphql-conformance-013.md
run_execution_spec::graphql-conformance-http-013.md
run_execution_spec::test-required-fields.md

test result: FAILED. 266 passed; 3 failed; 0 ignored; 0 measured; 0 filtered out; finished in 14.90s

@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 6.81ms 2.99ms 68.56ms 71.64%
Req/Sec 3.71k 138.86 4.28k 86.17%

443049 requests in 30.00s, 2.22GB read

Requests/sec: 14766.06

Transfer/sec: 75.79MB

Please sign in to comment.