Skip to content

Commit 8a40165

Browse files
luckasRanarisonYohe-Am
authored andcommitted
fix: selectAll infinite recursion (#948)
<!-- Pull requests are squashed and merged using: - their title as the commit message - their description as the commit body Having a good title and description is important for the users to get readable changelog. --> <!-- 1. Explain WHAT the change is about --> - Closes [MET-786](https://linear.app/metatypedev/issue/MET-786/typescript-client-selectall-infinite-recursion). <!-- 3. Explain HOW users should update their code --> --- - [x] The change comes with new or modified tests - [ ] Hard-to-understand functions have explanatory comments - [ ] End-user documentation is updated to reflect the change <!-- This is an auto-generated comment: release notes by coderabbit.ai --> - **New Features** - Added nested composite structure support across multiple client implementations - Enhanced selection handling for composite queries - Expanded type definitions for more complex data representations - **Bug Fixes** - Improved selection processing logic in client implementations - Updated version compatibility for SDK imports - **Chores** - Updated package dependencies to newer SDK versions - Reformatted and improved code readability across multiple files <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 0574243 commit 8a40165

File tree

14 files changed

+581
-222
lines changed

14 files changed

+581
-222
lines changed

examples/templates/deno/api/example.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Policy, t, typegraph } from "jsr:@typegraph/[email protected].7";
1+
import { Policy, t, typegraph } from "jsr:@typegraph/[email protected].9";
22
import { PythonRuntime } from "jsr:@typegraph/[email protected]/runtimes/python";
33
import { DenoRuntime } from "jsr:@typegraph/[email protected]/runtimes/deno";
44

src/metagen-client-rs/src/selection.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,8 +393,7 @@ where
393393
SelT: Selection + Into<CompositeSelection>,
394394
{
395395
fn all() -> Self {
396-
let sel = SelT::all();
397-
Self::Get(sel.into(), PhantomData)
396+
Self::Skip
398397
}
399398
}
400399
impl<ArgT, SelT, ATy> Selection for CompositeSelectArgs<ArgT, SelT, ATy>

src/metagen/src/client_py/static/client.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ def selection_to_nodes(
1717
parent_path: str,
1818
) -> typing.List["SelectNode[typing.Any]"]:
1919
out = []
20-
flags = selection.get("_")
21-
if flags is not None and not isinstance(flags, SelectionFlags):
20+
sub_flags = selection.get("_")
21+
if sub_flags is not None and not isinstance(sub_flags, SelectionFlags):
2222
raise Exception(
23-
f"selection field '_' should be of type SelectionFlags but found {type(flags)}"
23+
f"selection field '_' should be of type SelectionFlags but found {type(sub_flags)}"
2424
)
25-
select_all = True if flags is not None and flags.select_all else False
25+
select_all = True if sub_flags is not None and sub_flags.select_all else False
2626
found_nodes = set(selection.keys())
2727
for node_name, meta_fn in metas.items():
2828
found_nodes.discard(node_name)
@@ -104,7 +104,7 @@ def selection_to_nodes(
104104

105105
# flags are recursive for any subnode that's not specified
106106
if sub_selections is None:
107-
sub_selections = {"_": flags}
107+
sub_selections = {"_": sub_flags}
108108

109109
# selection types are always TypedDicts as well
110110
if not isinstance(sub_selections, dict):
@@ -119,6 +119,17 @@ def selection_to_nodes(
119119
raise Exception(
120120
"unreachable: union/either NodeMetas can't have subnodes"
121121
)
122+
123+
# skip non explicit composite selection when using select_all
124+
sub_flags = sub_selections.get("_")
125+
126+
if (
127+
isinstance(sub_flags, SelectionFlags)
128+
and sub_flags.select_all
129+
and instance_selection is None
130+
):
131+
continue
132+
122133
sub_nodes = selection_to_nodes(
123134
typing.cast("SelectionErased", sub_selections),
124135
meta.sub_nodes,

src/metagen/src/client_ts/static/mod.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ function _selectionToNodeSet(
107107
"unreachable: union/either NodeMetas can't have subnodes",
108108
);
109109
}
110+
111+
// skip non explicit composite selection when using selectAll
112+
if (subSelections?._ === "selectAll" && !instanceSelection) {
113+
continue;
114+
}
115+
110116
node.subNodes = _selectionToNodeSet(
111117
// assume it's a Selection. If it's an argument
112118
// object, mismatch between the node desc should hopefully

tests/injection/random_injection_test.ts

Lines changed: 130 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -17,151 +17,158 @@ const cases = [
1717
];
1818

1919
for (const testCase of cases) {
20-
Meta.test({
21-
name: testCase.testName,
22-
only: false,
23-
}, async (t) => {
24-
const engine = await t.engine(testCase.typegraph);
20+
Meta.test(
21+
{
22+
name: testCase.testName,
23+
only: false,
24+
},
25+
async (t) => {
26+
const engine = await t.engine(testCase.typegraph);
2527

26-
await t.should("generate random values", async () => {
27-
await gql`
28-
query {
29-
randomUser {
30-
id
31-
ean
32-
name
33-
age
34-
married
35-
birthday
36-
phone
37-
gender
38-
firstname
39-
lastname
40-
friends
41-
occupation
42-
street
43-
city
44-
postcode
45-
country
46-
uri
47-
hostname
28+
await t.should("generate random values", async () => {
29+
await gql`
30+
query {
31+
randomUser {
32+
id
33+
ean
34+
name
35+
age
36+
married
37+
birthday
38+
phone
39+
gender
40+
firstname
41+
lastname
42+
friends
43+
occupation
44+
street
45+
city
46+
postcode
47+
country
48+
uri
49+
hostname
50+
}
4851
}
49-
}
50-
`
51-
.expectData({
52-
randomUser: {
53-
id: "1069ace0-cdb1-5c1f-8193-81f53d29da35",
54-
ean: "0497901391205",
55-
name: "Landon Glover",
56-
age: 38,
57-
married: true,
58-
birthday: "2124-06-22T22:00:07.302Z",
59-
phone: "(587) 901-3720",
60-
gender: "Male",
61-
firstname: "Landon",
62-
lastname: "Mengoni",
63-
friends: ["Hettie", "Mary", "Lydia", "Ethel", "Jennie"],
64-
occupation: "Health Care Manager",
65-
street: "837 Wubju Drive",
66-
city: "Urbahfec",
67-
postcode: "IM9 9AD",
68-
country: "Indonesia",
69-
uri: "http://wubju.bs/ma",
70-
hostname: "wubju.bs",
71-
},
72-
})
73-
.on(engine);
74-
});
75-
});
52+
`
53+
.expectData({
54+
randomUser: {
55+
id: "1069ace0-cdb1-5c1f-8193-81f53d29da35",
56+
ean: "0497901391205",
57+
name: "Landon Glover",
58+
age: 38,
59+
married: true,
60+
birthday: "2125-06-22T22:00:07.302Z",
61+
phone: "(587) 901-3720",
62+
gender: "Male",
63+
firstname: "Landon",
64+
lastname: "Mengoni",
65+
friends: ["Hettie", "Mary", "Lydia", "Ethel", "Jennie"],
66+
occupation: "Health Care Manager",
67+
street: "837 Wubju Drive",
68+
city: "Urbahfec",
69+
postcode: "IM9 9AD",
70+
country: "Indonesia",
71+
uri: "http://wubju.bs/ma",
72+
hostname: "wubju.bs",
73+
},
74+
})
75+
.on(engine);
76+
});
77+
},
78+
);
7679
}
7780

7881
Meta.test("random injection on unions", async (t) => {
7982
const engine = await t.engine("injection/random_injection.py");
8083

8184
await t.should("work on random lists", async () => {
8285
await gql`
83-
query {
84-
randomList {
85-
names
86-
}
87-
}
88-
`.expectData({
89-
randomList: {
90-
names: [
91-
"Hettie Huff",
92-
"Ada Mills",
93-
"Ethel Marshall",
94-
"Emily Gonzales",
95-
"Lottie Barber",
96-
],
97-
},
98-
}).on(engine);
86+
query {
87+
randomList {
88+
names
89+
}
90+
}
91+
`
92+
.expectData({
93+
randomList: {
94+
names: [
95+
"Hettie Huff",
96+
"Ada Mills",
97+
"Ethel Marshall",
98+
"Emily Gonzales",
99+
"Lottie Barber",
100+
],
101+
},
102+
})
103+
.on(engine);
99104
});
100105

101106
await t.should(
102107
"generate random values for enums, either and union variants",
103108
async () => {
104109
await gql`
105-
query {
106-
testEnumStr {
107-
educationLevel
108-
},
109-
testEnumInt {
110-
bits
111-
},
112-
testEnumFloat {
113-
cents
114-
},
115-
testEither {
116-
toy {
117-
... on Toygun {
118-
color
119-
}
120-
... on Rubix {
121-
name,
122-
size
123-
}
110+
query {
111+
testEnumStr {
112+
educationLevel
124113
}
125-
},
126-
testUnion {
127-
field {
128-
... on Rgb {
129-
R
130-
G
131-
B
114+
testEnumInt {
115+
bits
116+
}
117+
testEnumFloat {
118+
cents
119+
}
120+
testEither {
121+
toy {
122+
... on Toygun {
123+
color
124+
}
125+
... on Rubix {
126+
name
127+
size
128+
}
132129
}
133-
... on Vec {
134-
x
135-
y
136-
z
130+
}
131+
testUnion {
132+
field {
133+
... on Rgb {
134+
R
135+
G
136+
B
137+
}
138+
... on Vec {
139+
x
140+
y
141+
z
142+
}
137143
}
138144
}
139145
}
140-
}
141-
`.expectData({
142-
testEnumStr: {
143-
educationLevel: "secondary",
144-
},
145-
testEnumInt: {
146-
bits: 0,
147-
},
148-
testEnumFloat: {
149-
cents: 0.5,
150-
},
151-
testEither: {
152-
toy: {
153-
name: "1*ajw]krgDnCzXD*N!Fx",
154-
size: 3336617896968192,
146+
`
147+
.expectData({
148+
testEnumStr: {
149+
educationLevel: "secondary",
155150
},
156-
},
157-
testUnion: {
158-
field: {
159-
B: 779226068287.488,
160-
G: 396901315143.2704,
161-
R: 895648526657.1263,
151+
testEnumInt: {
152+
bits: 0,
162153
},
163-
},
164-
}).on(engine);
154+
testEnumFloat: {
155+
cents: 0.5,
156+
},
157+
testEither: {
158+
toy: {
159+
name: "1*ajw]krgDnCzXD*N!Fx",
160+
size: 3336617896968192,
161+
},
162+
},
163+
testUnion: {
164+
field: {
165+
B: 779226068287.488,
166+
G: 396901315143.2704,
167+
R: 895648526657.1263,
168+
},
169+
},
170+
})
171+
.on(engine);
165172
},
166173
);
167174
});

tests/metagen/metagen_test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,24 @@ Meta.test(
595595
compositeNoArgs: postSchema,
596596
compositeArgs: postSchema,
597597
});
598+
const expectedSchemaC = zod.object({
599+
scalarOnly: zod.object({ scalar: zod.number() }),
600+
withStruct: zod.object({
601+
scalar: zod.number(),
602+
composite: zod.object({ value: zod.number() }),
603+
}),
604+
withStructNested: zod.object({
605+
scalar: zod.number(),
606+
composite: zod.object({
607+
value: zod.number(),
608+
nested: zod.object({ inner: zod.number() }),
609+
}),
610+
}),
611+
withList: zod.object({
612+
scalar: zod.number(),
613+
list: zod.array(zod.object({ value: zod.number() })),
614+
}),
615+
});
598616
const expectedSchema = zod.tuple([
599617
expectedSchemaQ,
600618
expectedSchemaQ,
@@ -607,6 +625,7 @@ Meta.test(
607625
compositeUnion2: zod.object({}),
608626
mixedUnion: zod.string(),
609627
}),
628+
expectedSchemaC,
610629
]);
611630
const cases = [
612631
{

0 commit comments

Comments
 (0)