Skip to content

Commit ca46519

Browse files
fix(typegraph): implement additional_props
1 parent 4115b43 commit ca46519

File tree

11 files changed

+40
-2
lines changed

11 files changed

+40
-2
lines changed

src/metagen/src/fdk_rs/stubs.rs

+1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ mod test {
116116
policies: Default::default(),
117117
id: vec![],
118118
required: vec![],
119+
additional_props: false,
119120
},
120121
base: TypeNodeBase {
121122
..default_type_node_base()

src/metagen/src/fdk_rs/types.rs

+8
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ mod test {
442442
id: vec![],
443443
// FIXME: remove required
444444
required: vec![],
445+
additional_props: false,
445446
},
446447
base: TypeNodeBase {
447448
title: "my_obj".into(),
@@ -617,6 +618,7 @@ pub enum MyUnion {
617618
id: vec![],
618619
required: ["obj_b"].into_iter().map(Into::into).collect(),
619620
policies: Default::default(),
621+
additional_props: false,
620622
},
621623
base: TypeNodeBase {
622624
title: "ObjA".into(),
@@ -629,6 +631,7 @@ pub enum MyUnion {
629631
policies: Default::default(),
630632
id: vec![],
631633
required: ["obj_c"].into_iter().map(Into::into).collect(),
634+
additional_props: false,
632635
},
633636
base: TypeNodeBase {
634637
title: "ObjB".into(),
@@ -641,6 +644,7 @@ pub enum MyUnion {
641644
policies: Default::default(),
642645
id: vec![],
643646
required: ["obj_a"].into_iter().map(Into::into).collect(),
647+
additional_props: false,
644648
},
645649
base: TypeNodeBase {
646650
title: "ObjC".into(),
@@ -672,6 +676,7 @@ pub struct ObjC {
672676
policies: Default::default(),
673677
id: vec![],
674678
required: ["obj_b"].into_iter().map(Into::into).collect(),
679+
additional_props: false,
675680
},
676681
base: TypeNodeBase {
677682
title: "ObjA".into(),
@@ -684,6 +689,7 @@ pub struct ObjC {
684689
policies: Default::default(),
685690
id: vec![],
686691
required: ["union_c"].into_iter().map(Into::into).collect(),
692+
additional_props: false,
687693
},
688694
base: TypeNodeBase {
689695
title: "ObjB".into(),
@@ -723,6 +729,7 @@ pub enum CUnion {
723729
policies: Default::default(),
724730
id: vec![],
725731
required: ["obj_b"].into_iter().map(Into::into).collect(),
732+
additional_props: false,
726733
},
727734
base: TypeNodeBase {
728735
title: "ObjA".into(),
@@ -735,6 +742,7 @@ pub enum CUnion {
735742
policies: Default::default(),
736743
id: vec![],
737744
required: ["either_c"].into_iter().map(Into::into).collect(),
745+
additional_props: false,
738746
},
739747
base: TypeNodeBase {
740748
title: "ObjB".into(),

src/metagen/src/tests/fixtures.rs

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ pub fn test_typegraph_2() -> Typegraph {
5858
policies: Default::default(),
5959
id: vec![],
6060
required: vec![],
61+
additional_props: false,
6162
},
6263
base: TypeNodeBase {
6364
..default_type_node_base()

src/typegate/src/engine/planner/args.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,7 @@ class ArgumentCollector {
675675
}
676676

677677
const unexpectedProps = Object.keys(fieldByKeys);
678-
if (unexpectedProps.length > 0) {
678+
if (!typ.additionalProps && unexpectedProps.length > 0) {
679679
throw new UnexpectedPropertiesError(
680680
unexpectedProps,
681681
this.currentNodeDetails,

src/typegate/src/engine/typecheck/inline_validators/object.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
import { check } from "./common.ts";
1010

1111
export function generateObjectValidator(
12-
_typeNode: ObjectNode,
12+
typeNode: ObjectNode,
1313
varName: string,
1414
path: string,
1515
keys: {
@@ -30,6 +30,7 @@ export function generateObjectValidator(
3030
`for (const key of ${varKeys}) {`,
3131
` if (${varRequired}.has(key)) { ${varRequired}.delete(key); continue; }`,
3232
` if (${varOptional}.has(key)) { ${varOptional}.delete(key); continue; }`,
33+
` if (${typeNode.additionalProps}) { continue; }`,
3334
` throw new Error(\`At "${path}": unexpected key: \${key}\`);`,
3435
`}`,
3536
check(

src/typegate/src/typegraph/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export type ObjectNode = {
6767
properties: {
6868
[k: string]: number;
6969
};
70+
additionalProps?: boolean;
7071
required?: string[];
7172
id?: string[];
7273
};

src/typegraph/core/src/typedef/struct_.rs

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ impl TypeConversion for Struct {
8181
id: self.data.find_id_fields()?,
8282
required: Vec::new(),
8383
policies: self.data.collect_policies(ctx)?,
84+
additional_props: self.data.additional_props,
8485
},
8586
})
8687
}

src/typegraph/core/src/typegraph.rs

+1
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ pub fn init(params: TypegraphInitParams) -> Result<()> {
137137
policies: Default::default(),
138138
id: vec![],
139139
required: vec![],
140+
additional_props: false,
140141
},
141142
}));
142143

src/typegraph/schema/src/types.rs

+4
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ pub struct FileTypeData {
152152

153153
#[skip_serializing_none]
154154
#[derive(Serialize, Deserialize, Clone, Debug)]
155+
#[serde(rename_all = "camelCase")]
155156
pub struct ObjectTypeData<Id = TypeId> {
156157
pub properties: IndexMap<String, Id>,
157158
pub id: Vec<String>,
@@ -160,6 +161,9 @@ pub struct ObjectTypeData<Id = TypeId> {
160161
#[serde(skip_serializing_if = "IndexMap::is_empty")]
161162
#[serde(default)]
162163
pub policies: IndexMap<String, Vec<PolicyIndices>>,
164+
#[serde(skip_serializing_if = "std::ops::Not::not")]
165+
#[serde(default)]
166+
pub additional_props: bool,
163167
}
164168

165169
#[skip_serializing_none]

tests/typecheck/input_validator_test.ts

+13
Original file line numberDiff line numberDiff line change
@@ -239,4 +239,17 @@ Meta.test("input validator compiler", async (t) => {
239239
})
240240
.on(e);
241241
});
242+
243+
await t.should("pass for any struct", async () => {
244+
await gql`
245+
query structs {
246+
simple: stringifyStruct(params: { str: "value", int: 0 })
247+
nested: stringifyStruct(params: { obj: { key: "value" } })
248+
}
249+
`
250+
.expectBody((body) => {
251+
assert(body.errors == null);
252+
})
253+
.on(e);
254+
});
242255
});

tests/typecheck/typecheck.py

+7
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ def typecheck(g: Graph):
106106

107107
empty = t.struct({}).rename("WillNotHaveAnyEffectLikeOtherScalars")
108108

109+
stringify_struct = deno.func(
110+
t.struct({"params": t.struct({}, additional_props=True, name="params")}),
111+
t.string(),
112+
code="({params}) => JSON.stringify(params)",
113+
)
114+
109115
g.expose(
110116
my_policy,
111117
createUser=create_user,
@@ -115,4 +121,5 @@ def typecheck(g: Graph):
115121
enums=deno.identity(enums),
116122
findProduct=deno.identity(product),
117123
emptyObjectOutput=deno.static(empty, {}),
124+
stringifyStruct=stringify_struct,
118125
)

0 commit comments

Comments
 (0)