Skip to content

Commit

Permalink
fix(gate): make __typename returns the variant name on unions (#838)
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 -->

- Add missing implementation for static injection for parameter
transformations on the typegate
- Solves
[MET-642](https://linear.app/metatypedev/issue/MET-642/gate-typename-on-union-selections-should-hold-member-title):
Fix the `__typename` result on union variants: return the variant name
instead of the parent type name

<!-- 2. Explain WHY the change cannot be made simpler -->



<!-- 3. Explain HOW users should update their code -->

#### Migration notes

N/A

#### Checklist

- [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
  • Loading branch information
Natoandro authored Sep 5, 2024
1 parent 67c1d0f commit c918cf6
Show file tree
Hide file tree
Showing 8 changed files with 389 additions and 569 deletions.
252 changes: 29 additions & 223 deletions .ghjk/deno.lock

Large diffs are not rendered by default.

582 changes: 271 additions & 311 deletions .ghjk/lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/typegate/src/engine/planner/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@ export class Planner {
}),
);
const unselectedVariants = new Set(selectableVariants.keys());

if (unselectedVariants.size === 0 && selectionSet.selections.length > 0) {
const path = this.formatPath(node.path);
throw new Error(`at ${path}: Unexpected selections`);
Expand Down Expand Up @@ -238,6 +237,7 @@ export class Planner {
...node,
path: parentPath,
typeIdx: idx,
parentStage: stage,
},
outputType.properties,
stage,
Expand Down
18 changes: 16 additions & 2 deletions src/typegate/src/engine/planner/parameter_transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import { type QueryFn, QueryFunction } from "../../libs/jsonpath.ts";
import type { TypeGraph } from "../../typegraph/mod.ts";
import { Type } from "../../typegraph/type_node.ts";
import type { ParameterTransformNode } from "../../typegraph/types.ts";
import { type ValidationContext, validationContext } from "../typecheck/common.ts";
import {
type ValidationContext,
validationContext,
} from "../typecheck/common.ts";
import { generateListValidator } from "../typecheck/inline_validators/list.ts";
import { generateNumberValidator } from "../typecheck/inline_validators/number.ts";
import {
Expand Down Expand Up @@ -174,9 +177,11 @@ class TransformerCompilationContext {
return this.#compileSecretsInjection(typeIdx, nodeData.key);
case "parent":
return this.#compileParentInjection(typeIdx, nodeData.parentIdx);
case "static":
return this.#compileStaticInjection(typeIdx, nodeData.valueJson);
default:
throw new Error(
`Unknown source: ${nodeData.source} at ${this.#path}`,
`Unknown source: ${(nodeData as any).source} at ${this.#path}`,
);
}
}
Expand Down Expand Up @@ -347,6 +352,15 @@ class TransformerCompilationContext {
return varName;
}

#compileStaticInjection(_typeIdx: number, valueJson: string) {
// VALIDATION
// - the value is validated AOT with the typegraph

const varName = this.#createVarName();
this.#collector.push(`const ${varName} = ${valueJson};`);
return varName;
}

#createVarName() {
return `_var${++this.#latestVarIndex}`;
}
Expand Down
23 changes: 16 additions & 7 deletions src/typegate/src/runtimes/deno/deno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,9 @@ export class DenoRuntime extends Runtime {
verbose: boolean,
): ComputeStage[] {
if (stage.props.node === "__typename") {
return [
stage.withResolver(() => {
const { parent: parentStage } = stage.props;
if (parentStage != null) {
return parentStage.props.outType.title;
}
const getTypename = () => {
const parentStage = stage.props.parent;
if (parentStage == null) {
switch (stage.props.operationType) {
case ast.OperationTypeNode.QUERY:
return "Query";
Expand All @@ -182,7 +179,19 @@ export class DenoRuntime extends Runtime {
`Unsupported operation type '${stage.props.operationType}'`,
);
}
}),
}

const idSlice = stage.id().slice(parentStage.id().length);
if (idSlice.startsWith("$")) {
return idSlice.split(".")[0].slice(1);
}
return parentStage.props.outType.title;
};

const typename = getTypename();

return [
stage.withResolver(() => typename),
];
}

Expand Down
2 changes: 1 addition & 1 deletion src/typegraph/python/typegraph/t.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ def serialize_apply_param_node(node: ApplyParamNode) -> Any:
if isinstance(node, ApplyFromArg):
return {"source": "arg", "name": node.name}
if isinstance(node, ApplyFromStatic):
return {"source": "static", "value": JsonLib.dumps(node.value)}
return {"source": "static", "value_json": JsonLib.dumps(node.value)}
if isinstance(node, ApplyFromContext):
return {"source": "context", "key": node.key}
if isinstance(node, ApplyFromSecret):
Expand Down
23 changes: 23 additions & 0 deletions tests/typename/typename.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ def typename(g: Graph):
code="() => ({ id: 12 })",
).with_policy(public)

u8 = t.integer(min=0, max=255, name="U8")

color = t.either(
[
t.enum(["red", "green", "blue"]).rename("NamedColor"),
t.string(pattern=r"^#[0-9a-f]{6}$").rename("HexColor"),
t.struct({"r": u8, "g": u8, "b": u8}).rename("RgbColor"),
]
)

g.expose(
denoUser=deno_user,
randomUser=randomUser,
Expand All @@ -31,4 +41,17 @@ def typename(g: Graph):
effect=effects.delete(),
).with_policy(public),
createUser=prisma.create(prisma_user).with_policy(public),
getRgbColor=deno.identity(t.struct({"color": color}))
.apply(
{
"color": g.set(
{
"r": 255,
"g": 0,
"b": 0,
}
)
}
)
.with_policy(public),
)
56 changes: 32 additions & 24 deletions tests/typename/typename_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
import { recreateMigrations } from "../utils/migrations.ts";
import { gql, Meta } from "../utils/mod.ts";

const secrets = {
POSTGRES: "postgresql://postgres:password@localhost:5432/db?schema=typename",
};

Meta.test("Typename", async (t) => {
const e = await t.engine("typename/typename.py", {
secrets: {
POSTGRES:
"postgresql://postgres:password@localhost:5432/db?schema=typename",
},
});
const e = await t.engine("typename/typename.py", { secrets });

await t.should("allow querying typename at root level", async () => {
await gql`
Expand All @@ -26,12 +25,7 @@ Meta.test("Typename", async (t) => {
});

Meta.test("Typename in deno runtime", async (t) => {
const e = await t.engine("typename/typename.py", {
secrets: {
POSTGRES:
"postgresql://postgres:password@localhost:5432/db?schema=typename",
},
});
const e = await t.engine("typename/typename.py", { secrets });

await t.should("allow querying typename in an object", async () => {
await gql`
Expand All @@ -51,12 +45,7 @@ Meta.test("Typename in deno runtime", async (t) => {
});

Meta.test("Typename in random runtime", async (t) => {
const e = await t.engine("typename/typename.py", {
secrets: {
POSTGRES:
"postgresql://postgres:password@localhost:5432/db?schema=typename",
},
});
const e = await t.engine("typename/typename.py", { secrets });

await t.should("allow querying typename in an object", async () => {
await gql`
Expand All @@ -76,12 +65,7 @@ Meta.test("Typename in random runtime", async (t) => {
});

Meta.test("Typename in prisma runtime", async (t) => {
const e = await t.engine("typename/typename.py", {
secrets: {
POSTGRES:
"postgresql://postgres:password@localhost:5432/db?schema=typename",
},
});
const e = await t.engine("typename/typename.py", { secrets });

await gql`
mutation a {
Expand Down Expand Up @@ -112,3 +96,27 @@ Meta.test("Typename in prisma runtime", async (t) => {
.on(e);
});
});

Meta.test("Typename on union", async (t) => {
const e = await t.engine("typename/typename.py", { secrets });

await t.should("get variant type name", async () => {
await gql`
query {
getRgbColor {
color {
... on RgbColor {
r g b __typename
}
}
}
}
`
.expectData({
getRgbColor: {
color: { r: 255, g: 0, b: 0, __typename: "RgbColor" },
},
})
.on(e);
});
});

0 comments on commit c918cf6

Please sign in to comment.