Skip to content

Commit

Permalink
fix: selectAll infinite recursion (#948)
Browse files Browse the repository at this point in the history
<!--
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 -->

#### Migration notes

---

- [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
-->

## Summary by CodeRabbit

- **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 -->
  • Loading branch information
luckasRanarison authored Jan 6, 2025
1 parent 87b70e5 commit 8c0022e
Show file tree
Hide file tree
Showing 16 changed files with 880 additions and 316 deletions.
2 changes: 1 addition & 1 deletion examples/templates/deno/api/example.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Policy, t, typegraph } from "jsr:@typegraph/sdk@0.5.0-rc.7";
import { Policy, t, typegraph } from "jsr:@typegraph/sdk@0.5.0-rc.9";
import { PythonRuntime } from "jsr:@typegraph/sdk@0.5.0-rc.9/runtimes/python";
import { DenoRuntime } from "jsr:@typegraph/sdk@0.5.0-rc.9/runtimes/deno";

Expand Down
3 changes: 1 addition & 2 deletions src/metagen-client-rs/src/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,7 @@ where
SelT: Selection + Into<CompositeSelection>,
{
fn all() -> Self {
let sel = SelT::all();
Self::Get(sel.into(), PhantomData)
Self::Skip
}
}
impl<ArgT, SelT, ATy> Selection for CompositeSelectArgs<ArgT, SelT, ATy>
Expand Down
21 changes: 16 additions & 5 deletions src/metagen/src/client_py/static/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ def selection_to_nodes(
parent_path: str,
) -> typing.List["SelectNode[typing.Any]"]:
out = []
flags = selection.get("_")
if flags is not None and not isinstance(flags, SelectionFlags):
sub_flags = selection.get("_")
if sub_flags is not None and not isinstance(sub_flags, SelectionFlags):
raise Exception(
f"selection field '_' should be of type SelectionFlags but found {type(flags)}"
f"selection field '_' should be of type SelectionFlags but found {type(sub_flags)}"
)
select_all = True if flags is not None and flags.select_all else False
select_all = True if sub_flags is not None and sub_flags.select_all else False
found_nodes = set(selection.keys())
for node_name, meta_fn in metas.items():
found_nodes.discard(node_name)
Expand Down Expand Up @@ -104,7 +104,7 @@ def selection_to_nodes(

# flags are recursive for any subnode that's not specified
if sub_selections is None:
sub_selections = {"_": flags}
sub_selections = {"_": sub_flags}

# selection types are always TypedDicts as well
if not isinstance(sub_selections, dict):
Expand All @@ -119,6 +119,17 @@ def selection_to_nodes(
raise Exception(
"unreachable: union/either NodeMetas can't have subnodes"
)

# skip non explicit composite selection when using select_all
sub_flags = sub_selections.get("_")

if (
isinstance(sub_flags, SelectionFlags)
and sub_flags.select_all
and instance_selection is None
):
continue

sub_nodes = selection_to_nodes(
typing.cast("SelectionErased", sub_selections),
meta.sub_nodes,
Expand Down
6 changes: 6 additions & 0 deletions src/metagen/src/client_ts/static/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ function _selectionToNodeSet(
"unreachable: union/either NodeMetas can't have subnodes",
);
}

// skip non explicit composite selection when using selectAll
if (subSelections?._ === "selectAll" && !instanceSelection) {
continue;
}

node.subNodes = _selectionToNodeSet(
// assume it's a Selection. If it's an argument
// object, mismatch between the node desc should hopefully
Expand Down
253 changes: 130 additions & 123 deletions tests/injection/random_injection_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,151 +17,158 @@ const cases = [
];

for (const testCase of cases) {
Meta.test({
name: testCase.testName,
only: false,
}, async (t) => {
const engine = await t.engine(testCase.typegraph);
Meta.test(
{
name: testCase.testName,
only: false,
},
async (t) => {
const engine = await t.engine(testCase.typegraph);

await t.should("generate random values", async () => {
await gql`
query {
randomUser {
id
ean
name
age
married
birthday
phone
gender
firstname
lastname
friends
occupation
street
city
postcode
country
uri
hostname
await t.should("generate random values", async () => {
await gql`
query {
randomUser {
id
ean
name
age
married
birthday
phone
gender
firstname
lastname
friends
occupation
street
city
postcode
country
uri
hostname
}
}
}
`
.expectData({
randomUser: {
id: "1069ace0-cdb1-5c1f-8193-81f53d29da35",
ean: "0497901391205",
name: "Landon Glover",
age: 38,
married: true,
birthday: "2124-06-22T22:00:07.302Z",
phone: "(587) 901-3720",
gender: "Male",
firstname: "Landon",
lastname: "Mengoni",
friends: ["Hettie", "Mary", "Lydia", "Ethel", "Jennie"],
occupation: "Health Care Manager",
street: "837 Wubju Drive",
city: "Urbahfec",
postcode: "IM9 9AD",
country: "Indonesia",
uri: "http://wubju.bs/ma",
hostname: "wubju.bs",
},
})
.on(engine);
});
});
`
.expectData({
randomUser: {
id: "1069ace0-cdb1-5c1f-8193-81f53d29da35",
ean: "0497901391205",
name: "Landon Glover",
age: 38,
married: true,
birthday: "2125-06-22T22:00:07.302Z",
phone: "(587) 901-3720",
gender: "Male",
firstname: "Landon",
lastname: "Mengoni",
friends: ["Hettie", "Mary", "Lydia", "Ethel", "Jennie"],
occupation: "Health Care Manager",
street: "837 Wubju Drive",
city: "Urbahfec",
postcode: "IM9 9AD",
country: "Indonesia",
uri: "http://wubju.bs/ma",
hostname: "wubju.bs",
},
})
.on(engine);
});
},
);
}

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

await t.should("work on random lists", async () => {
await gql`
query {
randomList {
names
}
}
`.expectData({
randomList: {
names: [
"Hettie Huff",
"Ada Mills",
"Ethel Marshall",
"Emily Gonzales",
"Lottie Barber",
],
},
}).on(engine);
query {
randomList {
names
}
}
`
.expectData({
randomList: {
names: [
"Hettie Huff",
"Ada Mills",
"Ethel Marshall",
"Emily Gonzales",
"Lottie Barber",
],
},
})
.on(engine);
});

await t.should(
"generate random values for enums, either and union variants",
async () => {
await gql`
query {
testEnumStr {
educationLevel
},
testEnumInt {
bits
},
testEnumFloat {
cents
},
testEither {
toy {
... on Toygun {
color
}
... on Rubix {
name,
size
}
query {
testEnumStr {
educationLevel
}
},
testUnion {
field {
... on Rgb {
R
G
B
testEnumInt {
bits
}
testEnumFloat {
cents
}
testEither {
toy {
... on Toygun {
color
}
... on Rubix {
name
size
}
}
... on Vec {
x
y
z
}
testUnion {
field {
... on Rgb {
R
G
B
}
... on Vec {
x
y
z
}
}
}
}
}
`.expectData({
testEnumStr: {
educationLevel: "secondary",
},
testEnumInt: {
bits: 0,
},
testEnumFloat: {
cents: 0.5,
},
testEither: {
toy: {
name: "1*ajw]krgDnCzXD*N!Fx",
size: 3336617896968192,
`
.expectData({
testEnumStr: {
educationLevel: "secondary",
},
},
testUnion: {
field: {
B: 779226068287.488,
G: 396901315143.2704,
R: 895648526657.1263,
testEnumInt: {
bits: 0,
},
},
}).on(engine);
testEnumFloat: {
cents: 0.5,
},
testEither: {
toy: {
name: "1*ajw]krgDnCzXD*N!Fx",
size: 3336617896968192,
},
},
testUnion: {
field: {
B: 779226068287.488,
G: 396901315143.2704,
R: 895648526657.1263,
},
},
})
.on(engine);
},
);
});
19 changes: 19 additions & 0 deletions tests/metagen/metagen_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,24 @@ Meta.test(
compositeNoArgs: postSchema,
compositeArgs: postSchema,
});
const expectedSchemaC = zod.object({
scalarOnly: zod.object({ scalar: zod.number() }),
withStruct: zod.object({
scalar: zod.number(),
composite: zod.object({ value: zod.number() }),
}),
withStructNested: zod.object({
scalar: zod.number(),
composite: zod.object({
value: zod.number(),
nested: zod.object({ inner: zod.number() }),
}),
}),
withList: zod.object({
scalar: zod.number(),
list: zod.array(zod.object({ value: zod.number() })),
}),
});
const expectedSchema = zod.tuple([
expectedSchemaQ,
expectedSchemaQ,
Expand All @@ -604,6 +622,7 @@ Meta.test(
compositeUnion2: zod.object({}),
mixedUnion: zod.string(),
}),
expectedSchemaC,
]);
const cases = [
{
Expand Down
2 changes: 1 addition & 1 deletion tests/metagen/typegraphs/identities/rs/fdk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl Router {
}

pub fn init(&self, args: InitArgs) -> Result<InitResponse, InitError> {
static MT_VERSION: &str = "0.5.0-rc.8";
static MT_VERSION: &str = "0.5.0-rc.9";
if args.metatype_version != MT_VERSION {
return Err(InitError::VersionMismatch(MT_VERSION.into()));
}
Expand Down
Loading

0 comments on commit 8c0022e

Please sign in to comment.