From d69f9ce602a251966e861671e5464c938a69b617 Mon Sep 17 00:00:00 2001 From: Natoandro Date: Sat, 9 Nov 2024 08:50:14 +0300 Subject: [PATCH] fix: fix optional field filter in apply (#909) - Fix the optional field filter on apply: resolve types before matching. #### Migration notes _No migrations needed._ --- - [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 --- deno.lock | 5 -- src/typegraph/core/src/params/apply.rs | 22 +++++---- tests/params/apply_test.ts | 68 +++++++++++++++++++++++++- tests/params/prisma_apply.py | 38 ++++++++++++++ 4 files changed, 118 insertions(+), 15 deletions(-) create mode 100644 tests/params/prisma_apply.py diff --git a/deno.lock b/deno.lock index 9a679f2d8..9b89ecb14 100644 --- a/deno.lock +++ b/deno.lock @@ -9,7 +9,6 @@ "jsr:@std/assert@^1.0.2": "jsr:@std/assert@1.0.4", "jsr:@std/assert@^1.0.4": "jsr:@std/assert@1.0.6", "jsr:@std/assert@^1.0.6": "jsr:@std/assert@1.0.6", - "jsr:@std/async@^1.0.3": "jsr:@std/async@1.0.3", "jsr:@std/bytes@^0.221.0": "jsr:@std/bytes@0.221.0", "jsr:@std/bytes@^1.0.2": "jsr:@std/bytes@1.0.2", "jsr:@std/bytes@^1.0.2-rc.3": "jsr:@std/bytes@1.0.2", @@ -47,7 +46,6 @@ "jsr:@std/semver@^1.0.1": "jsr:@std/semver@1.0.2", "jsr:@std/streams@0.221.0": "jsr:@std/streams@0.221.0", "jsr:@std/streams@1": "jsr:@std/streams@1.0.4", - "jsr:@std/streams@^1.0.2": "jsr:@std/streams@1.0.4", "jsr:@std/testing@^1.0.1": "jsr:@std/testing@1.0.2", "jsr:@std/url@^0.225.0": "jsr:@std/url@0.225.0", "jsr:@std/uuid@^1.0.1": "jsr:@std/uuid@1.0.2", @@ -114,9 +112,6 @@ "jsr:@std/internal@^1.0.4" ] }, - "@std/async@1.0.3": { - "integrity": "6ed64678db43451683c6c176a21426a2ccd21ba0269ebb2c36133ede3f165792" - }, "@std/bytes@0.221.0": { "integrity": "64a047011cf833890a4a2ab7293ac55a1b4f5a050624ebc6a0159c357de91966" }, diff --git a/src/typegraph/core/src/params/apply.rs b/src/typegraph/core/src/params/apply.rs index e90f31b45..92fa67534 100644 --- a/src/typegraph/core/src/params/apply.rs +++ b/src/typegraph/core/src/params/apply.rs @@ -3,7 +3,7 @@ use crate::errors::{ErrorContext, Result, TgError}; use crate::t::{self, TypeBuilder}; -use crate::types::{Type, TypeDef, TypeId}; +use crate::types::{AsTypeDefEx as _, TypeDef, TypeId}; use crate::wit::core::{ParameterTransform, TransformData}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -307,26 +307,30 @@ impl TransformDataBuildContext { .filter_map(|(&k, &type_id)| { TypeId(type_id) .as_type() - .map(|ty| { - if !matches!(ty, Type::Def(TypeDef::Optional(_))) { - Some(k) - } else { - None - } - }) .with_context(|| { format!( "Error while resolving type #{type_id} at the field {k:?} at {path:?}", path = stringify_path(&self.path).unwrap() ) }) + .and_then(|ty| { + ty.as_xdef() + .with_context(|| format!("resolving type: {}", ty.id().repr().unwrap())) + }) + .map(|ty| { + if !matches!(ty.type_def, TypeDef::Optional(_)) { + Some(k) + } else { + None + } + }) .transpose() }) .collect::>>()?; if !non_optional_fields.is_empty() { Err(format!( - "Missing non-optional fields {non_optional_fields:?} at {path:?}", + "missing non-optional fields {non_optional_fields:?} at {path:?}", path = stringify_path(&self.path).unwrap() ) .into()) diff --git a/tests/params/apply_test.ts b/tests/params/apply_test.ts index 5839ec91a..217a9b4fb 100644 --- a/tests/params/apply_test.ts +++ b/tests/params/apply_test.ts @@ -1,7 +1,10 @@ // Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. // SPDX-License-Identifier: MPL-2.0 -import { gql, Meta } from "../utils/mod.ts"; +import { randomPGConnStr } from "test-utils/database.ts"; +import { gql, Meta } from "test-utils/mod.ts"; +import { dropSchemas, recreateMigrations } from "test-utils/migrations.ts"; +import { assertEquals } from "@std/assert/equals"; Meta.test("(python (sdk): apply)", async (t) => { const e = await t.engine("params/apply.py", { @@ -253,3 +256,66 @@ Meta.test("nested context access", async (t) => { .on(e); }); }); + +Meta.test("apply with prisma generated types", async (t) => { + const { connStr, schema: _ } = randomPGConnStr(); + const e = await t.engine("params/prisma_apply.py", { + secrets: { + "POSTGRES": connStr, + }, + }); + await dropSchemas(e); + await recreateMigrations(e); + + let id: string = null!; + + await t.should("create user", async () => { + await gql` + mutation { + createUser( + name: "Alice" + email: "alice@example.com" + age: 30 + ) { + id + name + email + age + } + } + ` + .expectBody((body) => { + id = body.data.createUser.id; + assertEquals(body.data.createUser, { + id: id, + name: "Alice", + email: "alice@example.com", + age: 30, + }); + }) + .on(e); + }); + + await t.should("find user by id", async () => { + await gql` + query Q($id: ID!) { + findUser(id: $id) { + id + name + email + age + } + } + ` + .withVars({ id }) + .expectData({ + findUser: { + id, + name: "Alice", + email: "alice@example.com", + age: 30, + }, + }) + .on(e); + }); +}); diff --git a/tests/params/prisma_apply.py b/tests/params/prisma_apply.py new file mode 100644 index 000000000..78afba91b --- /dev/null +++ b/tests/params/prisma_apply.py @@ -0,0 +1,38 @@ +from typegraph import Graph, Policy, t, typegraph +from typegraph.providers import PrismaRuntime + + +@typegraph() +def prisma_apply(g: Graph): + prisma = PrismaRuntime("db", "POSTGRES") + public = Policy.public() + + user = t.struct( + { + "id": t.uuid(config=["auto"]).id(), + "name": t.string(), + "email": t.email(config=["unique"]), + "age": t.integer(), + }, + name="user", + ) + + g.expose( + public, + createUser=prisma.create(user).apply( + { + "data": { + "name": g.as_arg(), + "email": g.as_arg(), + "age": g.as_arg(), + } + } + ), + findUser=prisma.find_unique(user).apply( + { + "where": { + "id": g.as_arg(), + } + } + ), + )