diff --git a/.ghjk/lock.json b/.ghjk/lock.json index 9881a3abc..3f0e6de9f 100644 --- a/.ghjk/lock.json +++ b/.ghjk/lock.json @@ -1986,7 +1986,7 @@ } ] }, - "bciqkxr25pjywutrwjgdbuqvhukulifkffcybdcswmbdp6g57uyxalty": { + "bciqmz5es3gg7277grznhdd22rd7tx646occ5qzm455dezuo24mfod6y": { "provides": [ { "ty": "posix.envVar", @@ -2018,6 +2018,11 @@ "key": "V8_FORCE_DEBUG", "val": "true" }, + { + "ty": "posix.envVar", + "key": "RUST_JOBS", + "val": "8" + }, { "ty": "ghjk.ports.InstallSetRef", "setId": "ghjkEnvProvInstSet___dev" @@ -2066,7 +2071,7 @@ "_ecma": "bciqcqqs4e5l7nqt57e4bku3gjdxs2iruhfdl2ocayrkkcs4otx7ig7a", "_rust": "bciqof7ogmp2lx2bzmkbrtmdmrmj7seytb6bl2sb4uhnsxkf5v24m75i", "ci": "bciqbjavwy7rbire3zwlpgo2ifwzgnm6ywxqswnh6qxezwuvc4bqhrca", - "dev": "bciqkxr25pjywutrwjgdbuqvhukulifkffcybdcswmbdp6g57uyxalty", + "dev": "bciqmz5es3gg7277grznhdd22rd7tx646occ5qzm455dezuo24mfod6y", "oci": "bciqmkulmynzdor24gykcjc2vtu2vmzcgavyyytftuf4sibd7yutzmvy" } } diff --git a/deno.lock b/deno.lock index 532b9f700..f325caf56 100644 --- a/deno.lock +++ b/deno.lock @@ -8,6 +8,7 @@ "jsr:@std/assert@^0.221.0": "jsr:@std/assert@0.221.0", "jsr:@std/assert@^1.0.6": "jsr:@std/assert@1.0.9", "jsr:@std/assert@^1.0.9": "jsr:@std/assert@1.0.9", + "jsr:@std/async@1.0.9": "jsr:@std/async@1.0.9", "jsr:@std/async@^1.0.3": "jsr:@std/async@1.0.9", "jsr:@std/bytes@^0.221.0": "jsr:@std/bytes@0.221.0", "jsr:@std/bytes@^1.0.2": "jsr:@std/bytes@1.0.4", @@ -52,8 +53,11 @@ "npm:@sentry/node@7.70.0": "npm:@sentry/node@7.70.0", "npm:@sinonjs/fake-timers@13.0.5": "npm:@sinonjs/fake-timers@13.0.5", "npm:@types/node": "npm:@types/node@18.16.19", + "npm:ajv": "npm:ajv@8.17.1", + "npm:ajv-formats": "npm:ajv-formats@3.0.1", "npm:chance@1.1.11": "npm:chance@1.1.11", "npm:graphql@16.8.1": "npm:graphql@16.8.1", + "npm:js-yaml": "npm:js-yaml@3.14.1", "npm:json-schema-faker@0.5.3": "npm:json-schema-faker@0.5.3", "npm:lodash@4.17.21": "npm:lodash@4.17.21", "npm:marked": "npm:marked@15.0.3", @@ -61,6 +65,7 @@ "npm:multiformats@13.1.0": "npm:multiformats@13.1.0", "npm:pg@8.12.0": "npm:pg@8.12.0", "npm:validator@13.12.0": "npm:validator@13.12.0", + "npm:yaml": "npm:yaml@2.6.1", "npm:zod-validation-error@3.3.0": "npm:zod-validation-error@3.3.0_zod@3.23.8", "npm:zod@3.23.8": "npm:zod@3.23.8" }, @@ -339,6 +344,19 @@ "debug": "debug@4.3.6" } }, + "ajv-formats@3.0.1": { + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": {} + }, + "ajv@8.17.1": { + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "fast-deep-equal@3.1.3", + "fast-uri": "fast-uri@3.0.4", + "json-schema-traverse": "json-schema-traverse@1.0.0", + "require-from-string": "require-from-string@2.0.2" + } + }, "argparse@1.0.10": { "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dependencies": { @@ -379,6 +397,14 @@ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dependencies": {} }, + "fast-deep-equal@3.1.3": { + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dependencies": {} + }, + "fast-uri@3.0.4": { + "integrity": "sha512-G3iTQw1DizJQ5eEqj1CbFCWhq+pzum7qepkxU7rS1FGZDqjYKcrguo9XDRbV7EgPnn8CgaPigTq+NEjyioeYZQ==", + "dependencies": {} + }, "format-util@1.0.5": { "integrity": "sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg==", "dependencies": {} @@ -424,6 +450,10 @@ "ono": "ono@4.0.11" } }, + "json-schema-traverse@1.0.0": { + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dependencies": {} + }, "jsonpath-plus@7.2.0": { "integrity": "sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==", "dependencies": {} @@ -539,6 +569,10 @@ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "dependencies": {} }, + "require-from-string@2.0.2": { + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dependencies": {} + }, "seedrandom@3.0.5": { "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==", "dependencies": {} @@ -575,6 +609,10 @@ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dependencies": {} }, + "yaml@2.6.1": { + "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", + "dependencies": {} + }, "zod-validation-error@3.3.0_zod@3.23.8": { "integrity": "sha512-Syib9oumw1NTqEv4LT0e6U83Td9aVRk9iTXPUQr1otyV1PuXQKOvOwhMNqZIq5hluzHP2pMgnOmHEo7kPdI2mw==", "dependencies": { @@ -1220,6 +1258,8 @@ "https://esm.sh/@aws-sdk/lib-storage@3.700.0?pin=v135": "20499413966c9d494f4bff63361359e095f174c4a41ee79da3a0fbeb62dc947f", "https://esm.sh/@aws-sdk/s3-request-presigner@3.645.0?pin=v135": "03cf57cb951aece8cb946fb31f910b5d96fcb54aadc15973cee8fa079a9783a1", "https://esm.sh/@aws-sdk/s3-request-presigner@3.700.0?pin=v135": "806a2f5f0c65996434f031fbeb3983ee271239e9b22c70cf3624b79b2667cdce", + "https://esm.sh/ajv-formats@3.0.1": "d4eb830ffcadb8a7d19140b3bbd4e78c79bd700deb22e9ce2319437291eedef0", + "https://esm.sh/ajv@8.12.0": "cc1a73af661466c7f4e6a94d93ece78542d700f2165bdb16a531e9db8856c5aa", "https://esm.sh/ajv@8.12.0?pin=v131": "f8dc3d8e4d6d69f48381749333cc388e54177f66601125b43246c3e43d3145d6", "https://esm.sh/jszip@3.7.1": "f3872a819b015715edb05f81d973b5cd05d3d213d8eb28293ca5471fe7a71773", "https://esm.sh/v131/ajv@8.12.0/denonext/ajv.mjs": "6ec3e0f3d7a95672c96274f6aece644b6b5541e8c2409aed36b59853529a01cf", @@ -1364,12 +1404,21 @@ "https://esm.sh/v135/@smithy/util-utf8@3.0.0/denonext/util-utf8.mjs": "abe704ed8c4266b29906116ef723b98e8729078537b252c9a213ad373559488a", "https://esm.sh/v135/@smithy/util-waiter@3.1.2/denonext/util-waiter.mjs": "8bff673e4c8b620b34f59cbfa0e6c92de95b3c00190861b5b2cb113923bf8288", "https://esm.sh/v135/@smithy/util-waiter@3.1.9/denonext/util-waiter.mjs": "42a09843870abe258a66a3f381fafdef5fb1ea33d949b760c33451ff5b965d7f", + "https://esm.sh/v135/ajv-formats@3.0.1/denonext/ajv-formats.mjs": "8edbd78b344ad19468e5cb43b5b2eef05600b5d6dfadf0967fa8969dbd6212d6", + "https://esm.sh/v135/ajv@8.12.0/denonext/ajv.mjs": "4645df9093d0f8be0e964070a4a7aea8adea06e8883660340931f7a3f979fc65", + "https://esm.sh/v135/ajv@8.12.0/denonext/dist/compile/codegen.js": "d981238e5b1e78217e1c6db59cbd594369279722c608ed630d08717ee44edd84", + "https://esm.sh/v135/ajv@8.17.1/denonext/ajv.mjs": "704f78083e7ac737f67c38f37a3473d135e2204219db8ccd992a13f6a1f4e212", + "https://esm.sh/v135/ajv@8.17.1/denonext/dist/compile/codegen.js": "e29f49739d38b9198fb515cf612500335a2b523b8ad170e29a5ef85a7beb6a50", "https://esm.sh/v135/bowser@2.11.0/denonext/bowser.mjs": "3fd0c5d68c4bb8b3243c1b0ac76442fa90f5e20ee12773ce2b2f476c2e7a3615", + "https://esm.sh/v135/fast-deep-equal@3.1.3/denonext/fast-deep-equal.mjs": "6313b3e05436550e1c0aeb2a282206b9b8d9213b4c6f247964dd7bb4835fb9e5", + "https://esm.sh/v135/fast-uri@3.0.1/denonext/fast-uri.mjs": "fac5187f11b297bc2be866610ab5acefa2bce51aa681e8d5f28d24f0c76cbd7d", "https://esm.sh/v135/fast-xml-parser@4.4.1/denonext/fast-xml-parser.mjs": "506f0ae0ce83e4664b4e2a3bf3cde30b3d44c019012938ab12b76fa38353e864", + "https://esm.sh/v135/json-schema-traverse@1.0.0/denonext/json-schema-traverse.mjs": "c5da8353bc014e49ebbb1a2c0162d29969a14c325da19644e511f96ba670cc45", "https://esm.sh/v135/jszip@3.7.1/denonext/jszip.mjs": "d31d7f9e0de9c6db3c07ca93f7301b756273d4dccb41b600461978fc313504c9", "https://esm.sh/v135/strnum@1.0.5/denonext/strnum.mjs": "1ffef4adec2f74139e36a2bfed8381880541396fe1c315779fb22e081b17468b", "https://esm.sh/v135/tslib@2.6.2/denonext/tslib.mjs": "29782bcd3139f77ec063dc5a9385c0fff4a8d0a23b6765c73d9edeb169a04bf1", "https://esm.sh/v135/tslib@2.6.3/denonext/tslib.mjs": "0834c22e9fbf95f6a5659cc2017543f7d41aa880f24ab84cb11d24e6bee99303", + "https://esm.sh/v135/uri-js@4.4.1/denonext/uri-js.mjs": "901d462f9db207376b39ec603d841d87e6b9e9568ce97dfaab12aa77d0f99f74", "https://esm.sh/v135/uuid@9.0.1/denonext/uuid.mjs": "7d7d3aa57fa136e2540886654c416d9da10d8cfebe408bae47fd47070f0bfb2a", "https://raw.githubusercontent.com/levibostian/deno-udd/ignore-prerelease/deps.ts": "2b20d8c142749898e0ad5e4adfdc554dbe1411e8e5ef093687767650a1073ff8", "https://raw.githubusercontent.com/levibostian/deno-udd/ignore-prerelease/mod.ts": "3ef8bb10b88541586bae7d92c32f469627d3a6a799fa8a897ac819b2f7dd95e8", diff --git a/docs/metatype.dev/docs/reference/typegate/synchronization/index.mdx b/docs/metatype.dev/docs/reference/typegate/synchronization/index.mdx index ae8dc1892..37061edbb 100644 --- a/docs/metatype.dev/docs/reference/typegate/synchronization/index.mdx +++ b/docs/metatype.dev/docs/reference/typegate/synchronization/index.mdx @@ -69,6 +69,7 @@ Synchronization variable names start with `SYNC_`. | SYNC\__S3_SECRET_KEY (\_Required_) | Access key secret for the S3 store credentials; | | SYNC\__S3_PATH_STYLE (\_Optional_) | `true` or `false`, force path style if `true`. | | SYNC\__S3_BUCKET (\_Required_) | The bucket to be used for the system (dedicated). | +| SYNC\__FORCE_REMOVE (\_Optional_) | `true` or `false`, Undeploy cached typegraphs at boot | ## Synchronized mode features diff --git a/examples/typegraphs/metagen/rs/fdk.rs b/examples/typegraphs/metagen/rs/fdk.rs index a67d003e4..e6c756fe3 100644 --- a/examples/typegraphs/metagen/rs/fdk.rs +++ b/examples/typegraphs/metagen/rs/fdk.rs @@ -109,7 +109,7 @@ impl Router { } pub fn init(&self, args: InitArgs) -> Result { - 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())); } diff --git a/ghjk.ts b/ghjk.ts index bb7a4cf3d..82986cbe8 100644 --- a/ghjk.ts +++ b/ghjk.ts @@ -109,6 +109,8 @@ env("dev") // debug v8 to fix MET-633: segementation fault bug with custom deno rt // doesn't appear in CI so only dev envs get it .var("V8_FORCE_DEBUG", "true") + // limit to 8 cores to avoid exhausting memory + .var("RUST_JOBS", "8") .install( ports.act(), ports.cargobi({ crateName: "whiz", locked: true }), diff --git a/src/common/src/typegraph/runtimes/deno.rs b/src/common/src/typegraph/runtimes/deno.rs index c370c06d7..073c7ee04 100644 --- a/src/common/src/typegraph/runtimes/deno.rs +++ b/src/common/src/typegraph/runtimes/deno.rs @@ -1,6 +1,7 @@ // Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. // SPDX-License-Identifier: MPL-2.0 +use anyhow::{anyhow, Context, Result}; use indexmap::IndexMap; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -11,6 +12,47 @@ pub struct FunctionMatData { pub script: String, } +#[derive(Serialize, Deserialize, Clone, Debug, Hash, PartialEq, Eq)] +#[serde(tag = "type", content = "value")] +#[serde(rename_all = "snake_case")] +pub enum ContextCheckX { + NotNull, + Value(String), + Pattern(String), +} + +#[derive(PartialEq, Eq, Hash, Serialize, Deserialize, Clone, Debug)] +#[serde(rename_all = "snake_case")] +#[serde(tag = "name", content = "param")] +pub enum PredefinedFunctionMatData { + Identity, + True, + False, + Allow, + Deny, + Pass, + InternalPolicy, + ContextCheck { key: String, value: ContextCheckX }, +} + +#[derive(Serialize)] +struct PredefinedFunctionMatDataRaw { + name: String, + param: Option, +} + +impl PredefinedFunctionMatData { + pub fn from_raw(name: String, param: Option) -> Result { + let param = param + .map(|p| serde_json::from_str(&p)) + .transpose() + .context("invalid predefined function materializer parameter")?; + let value = serde_json::to_value(&PredefinedFunctionMatDataRaw { name, param })?; + serde_json::from_value(value) + .map_err(|e| anyhow!("invalid predefined function materializer: {e:?}")) + } +} + #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "camelCase")] pub struct ModuleMatData { diff --git a/src/typegate/src/config.ts b/src/typegate/src/config.ts index dd32d7e76..6ac4ad2f3 100644 --- a/src/typegate/src/config.ts +++ b/src/typegate/src/config.ts @@ -117,6 +117,7 @@ export function transformSyncConfig(raw: SyncConfig): SyncConfigX { redis, s3, s3Bucket: raw.s3_bucket, + forceRemove: raw.force_remove }; } diff --git a/src/typegate/src/config/types.ts b/src/typegate/src/config/types.ts index 5397e05ac..a8102255c 100644 --- a/src/typegate/src/config/types.ts +++ b/src/typegate/src/config/types.ts @@ -115,12 +115,14 @@ export const syncConfigSchema = z.object({ s3_access_key: refineEnvVar("SYNC_S3_ACCESS_KEY"), s3_secret_key: refineEnvVar("SYNC_S3_SECRET_KEY"), s3_path_style: zBooleanString.default(false), + force_remove: zBooleanString.default(false), }); export type SyncConfig = z.infer; export type SyncConfigX = { redis: RedisConnectOptions; s3: S3ClientConfig; s3Bucket: string; + forceRemove?: boolean }; export type TypegateConfig = { diff --git a/src/typegate/src/main.ts b/src/typegate/src/main.ts index f6e1f0e1e..55866e732 100644 --- a/src/typegate/src/main.ts +++ b/src/typegate/src/main.ts @@ -53,7 +53,6 @@ try { base: defaultTypegateConfigBase, }); const typegate = await Typegate.init(config); - await SystemTypegraph.loadAll(typegate, !globalConfig.packaged); const server = Deno.serve( diff --git a/src/typegate/src/runtimes/deno/deno.ts b/src/typegate/src/runtimes/deno/deno.ts index c98ad0f50..5a698ebd8 100644 --- a/src/typegate/src/runtimes/deno/deno.ts +++ b/src/typegate/src/runtimes/deno/deno.ts @@ -18,7 +18,6 @@ import type { } from "../../typegraph/types.ts"; import * as ast from "graphql/ast"; import { InternalAuth } from "../../services/auth/protocols/internal.ts"; -import { DenoMessenger } from "./deno_messenger.ts"; import type { Task } from "./shared_types.ts"; import { path } from "compress/deps.ts"; import { globalConfig as config } from "../../config.ts"; @@ -27,17 +26,46 @@ import { PolicyResolverOutput } from "../../engine/planner/policies.ts"; import { getInjectionValues } from "../../engine/planner/injection_utils.ts"; import DynamicInjection from "../../engine/injection/dynamic.ts"; import { getLogger } from "../../log.ts"; +import { WorkerManager } from "./worker_manager.ts"; const logger = getLogger(import.meta); -const predefinedFuncs: Record>> = { - identity: ({ _, ...args }) => args, - true: () => true, - false: () => false, - allow: () => "ALLOW" as PolicyResolverOutput, - deny: () => "DENY" as PolicyResolverOutput, - pass: () => "PASS" as PolicyResolverOutput, - internal_policy: ({ _: { context } }) => context.provider === "internal" ? "ALLOW" : "PASS" as PolicyResolverOutput, +const predefinedFuncs: Record< + string, + (param: any) => Resolver> +> = { + identity: () => ({ _, ...args }) => args, + true: () => () => true, + false: () => () => false, + allow: () => () => "ALLOW" as PolicyResolverOutput, + deny: () => () => "DENY" as PolicyResolverOutput, + pass: () => () => "PASS" as PolicyResolverOutput, + internal_policy: () => ({ _: { context } }) => + context.provider === "internal" ? "ALLOW" : "PASS" as PolicyResolverOutput, + context_check: ({ key, value }) => { + let check: (value: any) => boolean; + switch (value.type) { + case "not_null": + check = (v) => v != null; + break; + case "value": + check = (v) => v === value.value; + break; + case "pattern": + check = (v) => v != null && new RegExp(value.value).test(v); + break; + default: + throw new Error("unreachable"); + } + const path = key.split("."); + return ({ _: { context } }) => { + let value: any = context; + for (const segment of path) { + value = value?.[segment]; + } + return check(value) ? "PASS" : "DENY" as PolicyResolverOutput; + }; + }, }; export class DenoRuntime extends Runtime { @@ -46,8 +74,7 @@ export class DenoRuntime extends Runtime { uuid: string, private tg: TypeGraphDS, private typegate: Typegate, - private w: DenoMessenger, - private registry: Map, + private workerManager: WorkerManager, private secrets: Record, ) { super(typegraphName, uuid); @@ -138,28 +165,16 @@ export class DenoRuntime extends Runtime { } } - const w = new DenoMessenger( - name, - { - ...(args.permissions ?? {}), - read: [basePath], - } as Deno.PermissionOptionsObject, - false, - ops, - typegate.config.base, - ); - - if (Deno.env.get("DENO_TESTING") === "true") { - w.disableLazyness(); - } + const workerManager = new WorkerManager({ + timeout_ms: typegate.config.base.timer_max_timeout_ms, + }); const rt = new DenoRuntime( typegraphName, uuid, tg, typegate, - w, - registry, + workerManager, secrets, ); @@ -167,7 +182,7 @@ export class DenoRuntime extends Runtime { } async deinit(): Promise { - await this.w.terminate(); + // await this.workerManager.deinit(); } materialize( @@ -241,7 +256,7 @@ export class DenoRuntime extends Runtime { if (!func) { throw new Error(`predefined function ${mat.data.name} not found`); } - return func; + return func(mat.data.param); } if (mat.name === "static") { @@ -257,7 +272,10 @@ export class DenoRuntime extends Runtime { const modMat = this.tg.materializers[mat.data.mod as number]; const entryPoint = this.tg.meta.artifacts[modMat.data.entryPoint as string]; - const op = this.registry.get(entryPoint.hash)!; + const depMetas = (modMat.data.deps as string[]).map((dep) => + createArtifactMeta(this.typegraphName, this.tg.meta.artifacts[dep]) + ); + const moduleMeta = createArtifactMeta(this.typegraphName, entryPoint); return async ({ _: { @@ -269,33 +287,33 @@ export class DenoRuntime extends Runtime { }) => { const token = await InternalAuth.emit(this.typegate.cryptoKeys); - return await this.w.execute( - op, + // TODO cache?? + const entryModulePath = await this.typegate.artifactStore.getLocalPath( + moduleMeta, + depMetas, + ); + + return await this.workerManager.callFunction( + mat.data.name as string, + entryModulePath, + entryPoint.path, + args, { - type: "import_func", - args, - internals: { - parent, - context, - secrets, - effect: mat.effect.effect ?? null, - meta: { - url: `${url.protocol}//${url.host}/${this.typegraphName}`, - token, - }, - headers, + parent, + context, + secrets, + effect: mat.effect.effect ?? null, + meta: { + url: `${url.protocol}//${url.host}/${this.typegraphName}`, + token, }, - name: mat.data.name as string, - verbose, + headers, }, - [], - pulseCount, ); }; } if (mat.name === "function") { - const op = this.registry.get(mat.data.script as string)!; return async ({ _: { context, @@ -306,26 +324,29 @@ export class DenoRuntime extends Runtime { }) => { const token = await InternalAuth.emit(this.typegate.cryptoKeys); - return await this.w.execute( - op, + const modulePath = await this.typegate.artifactStore.getInlineArtifact( + this.typegraphName, + mat.data.script as string, + ".ts", + exportInlineFunction("inlineFunction"), + ); + + return await this.workerManager.callFunction( + "inlineFunction", + modulePath, + "tg", + args, { - type: "func", - args, - internals: { - parent, - context, - secrets, - effect: mat.effect.effect ?? null, - meta: { - url: `${url.protocol}//${url.host}/${this.typegraphName}`, - token, - }, - headers, + parent, + context, + secrets, + effect: mat.effect.effect ?? null, + meta: { + url: `${url.protocol}//${url.host}/${this.typegraphName}`, + token, }, - verbose, + headers, }, - [], - pulseCount, ); }; } @@ -365,6 +386,18 @@ export class DenoRuntime extends Runtime { } } +function exportInlineFunction(name = "fn", symbol = "_my_lambda") { + if (!name.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) { + throw new Error(`Invalid identifier: ${name}`); + } + if (!symbol.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) { + throw new Error(`Invalid identifier: ${symbol}`); + } + return (code: string) => { + return `${code}\nexport const ${name} = ${symbol};`; + }; +} + function getInjectionData(d: InjectionData) { if ("value" in d) { return d.value; diff --git a/src/typegate/src/runtimes/deno/deno_messenger.ts b/src/typegate/src/runtimes/deno/deno_messenger.ts deleted file mode 100644 index 27050f26e..000000000 --- a/src/typegate/src/runtimes/deno/deno_messenger.ts +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. -// SPDX-License-Identifier: MPL-2.0 - -import * as Sentry from "sentry"; -import { envSharedWithWorkers } from "../../config/shared.ts"; -import type { Task } from "./shared_types.ts"; -import { - type AsyncMessengerConfig, - LazyAsyncMessenger, -} from "../patterns/messenger/lazy_async_messenger.ts"; - -export class DenoMessenger extends LazyAsyncMessenger { - constructor( - name: string, - permissions: Deno.PermissionOptionsObject, - lazy: boolean, - ops: Map, - config: AsyncMessengerConfig, - ) { - super( - (receive) => { - const worker = new Worker(import.meta.resolve("./worker.ts"), { - type: "module", - deno: { - namespace: false, - permissions: { - // overrideable default permissions - hrtime: false, - net: true, - // on request permissions - read: false, // default read permission - ...permissions, - // non-overridable permissions (security between typegraphs) - run: false, - write: false, - ffi: false, - env: envSharedWithWorkers, // use secrets on the materializer instead - }, - }, - } as WorkerOptions); - worker.onmessage = async (event) => { - await receive(event.data); - }; - worker.onerror = (error) => { - console.error(error.message); - Sentry.captureException(error.message); - throw error; - }; - worker.onmessageerror = (error) => { - console.error(error); - Sentry.captureException(error); - throw error; - }; - - worker.postMessage({ - name, - }); - return worker; - }, - ops, - (broker, message) => { - broker.postMessage(message); - }, - (broker) => { - broker.terminate(); - }, - config, - ); - - if (lazy) { - this.enableLazyness(); - } - } -} diff --git a/src/typegate/src/runtimes/deno/types.ts b/src/typegate/src/runtimes/deno/types.ts new file mode 100644 index 000000000..ede8ec480 --- /dev/null +++ b/src/typegate/src/runtimes/deno/types.ts @@ -0,0 +1,21 @@ +// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +// SPDX-License-Identifier: MPL-2.0 + +import { TaskContext } from "./shared_types.ts"; + +export type TaskSpec = { + modulePath: string; + functionName: string; +}; + +export type DenoMessage = { + type: "CALL"; + modulePath: string; + functionName: string; + args: unknown; + internals: TaskContext; +}; + +export type DenoEvent = + | { type: "SUCCESS"; result: unknown } + | { type: "FAILURE"; error: string; exception: Error | undefined }; diff --git a/src/typegate/src/runtimes/deno/worker.ts b/src/typegate/src/runtimes/deno/worker.ts index 424e29537..5ac3498d9 100644 --- a/src/typegate/src/runtimes/deno/worker.ts +++ b/src/typegate/src/runtimes/deno/worker.ts @@ -1,116 +1,54 @@ // Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. // SPDX-License-Identifier: MPL-2.0 -// bring the unstable WorkerOptions api into scope -/// - -import { getLogger } from "../../log.ts"; -import { make_internal } from "../../worker_utils.ts"; -import type { Answer, Message } from "../patterns/messenger/types.ts"; import { toFileUrl } from "@std/path/to-file-url"; - -import type { - FuncTask, - ImportFuncTask, - RegisterFuncTask, - RegisterImportFuncTask, - Task, - TaskExec, -} from "./shared_types.ts"; - -let logger = getLogger("worker"); - -let initData = null as unknown as { name: string }; - -type TaskModule = Record; -const registry: Map = new Map(); +import { DenoMessage } from "./types.ts"; +import { errorToString, make_internal } from "../../worker_utils.ts"; const isTest = Deno.env.get("DENO_TESTING") === "true"; + const additionalHeaders = isTest ? { connection: "close" } : { connection: "keep-alive" }; -async function import_func(op: number, task: ImportFuncTask) { - const { name, args, internals, verbose } = task; - - if (!registry.has(op)) { - throw new Error(`no module registered with id ${op}`); - } - - verbose && logger.info(`exec func "${name}" from module ${op}`); - const mod = registry.get(op)! as TaskModule; - if (name in mod && typeof mod[name] === "function") { - return await mod[name]( - args, - internals, - make_internal(internals, additionalHeaders), - ); - } - throw new Error(`"${name}" is not a valid method`); -} - -async function func(op: number, task: FuncTask) { - const { args, internals, verbose } = task; - - if (!registry.has(op)) { - throw new Error(`no function registered with id ${op}`); - } - - verbose && logger.info(`exec func "${op}"`); - const fn = registry.get(op)! as TaskExec; - return await fn(args, internals, make_internal(internals, additionalHeaders)); -} - -async function register_import_func(_: null, task: RegisterImportFuncTask) { - const { modulePath, verbose, op } = task; - verbose && - logger.info(`register import func "${op}" from "${modulePath.toString()}`); - - registry.set(op, await import(toFileUrl(modulePath).toString())); -} - -function register_func(_: null, task: RegisterFuncTask) { - const { fnCode, verbose, op } = task; - verbose && logger.info(`register func "${op}"`); - - registry.set( - op, - new Function(`"use strict"; ${fnCode}; return _my_lambda;`)(), - ); -} - -const taskList: any = { - register_func, - register_import_func, - import_func, - func, -}; - -function answer(res: Answer) { - self.postMessage(res); -} - -self.onmessage = async (event: MessageEvent>) => { - if (initData == null) { - initData = event.data as typeof initData; - logger = getLogger(`worker (${initData.name})`); - return; - } - - const { id, op, data: task } = event.data; - const exec = taskList[task.type]; - - if (exec == null) { - const error = `unsupported operation found "${op}"`; - logger.error(error); - answer({ id, error }); - } - - try { - const data = await exec(op, task); - answer({ id, data }); - } catch (err) { - logger.error(err); - answer({ id, error: String(err) }); +self.onmessage = async function (event: MessageEvent) { + const { type, modulePath, functionName, args, internals } = event.data; + switch (type) { + case "CALL": { + const module = await import(toFileUrl(modulePath).toString()); + const fn = module[functionName]; + + if (typeof fn !== "function") { + postMessage({ + type: "FAILURE", + error: `Function "${functionName}" not found`, + }); + return; + } + + try { + const result = await fn( + args, + internals, + make_internal(internals, additionalHeaders), + ); + self.postMessage({ + type: "SUCCESS", + result, + }); + } catch (e) { + self.postMessage({ + type: "FAILURE", + error: errorToString(e), + exception: e, + }); + } + + break; + } + + default: + // unreachable + throw new Error(`Unknown message type: ${type}`); } }; diff --git a/src/typegate/src/runtimes/deno/worker_manager.ts b/src/typegate/src/runtimes/deno/worker_manager.ts new file mode 100644 index 000000000..7b16fca1c --- /dev/null +++ b/src/typegate/src/runtimes/deno/worker_manager.ts @@ -0,0 +1,77 @@ +// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +// SPDX-License-Identifier: MPL-2.0 + +import { getLogger } from "../../log.ts"; +import { DenoWorker } from "../patterns/worker_manager/deno.ts"; +import { + BaseWorkerManager, + createTaskId, +} from "../patterns/worker_manager/mod.ts"; +import { TaskId } from "../patterns/worker_manager/types.ts"; +import { TaskContext } from "./shared_types.ts"; +import { DenoEvent, DenoMessage, TaskSpec } from "./types.ts"; + +const logger = getLogger(import.meta, "WARN"); + +export type WorkerManagerConfig = { + timeout_ms: number; +}; + +export class WorkerManager + extends BaseWorkerManager { + constructor(private config: WorkerManagerConfig) { + super( + (taskId: TaskId) => { + return new DenoWorker(taskId, import.meta.resolve("./worker.ts")); + }, + ); + } + + callFunction( + name: string, + modulePath: string, + relativeModulePath: string, + args: unknown, + internalTCtx: TaskContext, + ) { + const taskId = createTaskId(`${name}@${relativeModulePath}`); + this.createWorker(name, taskId, { + modulePath, + functionName: name, + }); + this.sendMessage(taskId, { + type: "CALL", + modulePath, + functionName: name, + args, + internals: internalTCtx, + }); + + return new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + this.destroyWorker(name, taskId); + reject(new Error(`${this.config.timeout_ms}ms timeout exceeded`)); + }, this.config.timeout_ms); + + const handler: (event: DenoEvent) => void = (event) => { + clearTimeout(timeoutId); + this.destroyWorker(name, taskId); + switch (event.type) { + case "SUCCESS": + resolve(event.result); + break; + case "FAILURE": + reject(event.exception ?? event.error); + break; + } + }; + + const { worker } = this.getTask(taskId); + worker.listen(handler); + }); + } + + override logMessage(taskId: TaskId, msg: DenoMessage) { + logger.info(`Task "${taskId}" received message: ${msg.type}`); + } +} diff --git a/src/typegate/src/runtimes/patterns/messenger/async_messenger.ts b/src/typegate/src/runtimes/patterns/messenger/async_messenger.ts deleted file mode 100644 index d77a4c360..000000000 --- a/src/typegate/src/runtimes/patterns/messenger/async_messenger.ts +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. -// SPDX-License-Identifier: MPL-2.0 - -import { getLogger } from "../../../log.ts"; -import type { Answer, Message, TaskData } from "./types.ts"; -import { maxi32 } from "../../../utils.ts"; -import type { TypegateConfigBase } from "../../../config/types.ts"; - -const logger = getLogger(import.meta); - -export type MessengerStart = ( - receive: (answer: Answer) => Promise, -) => Broker; - -export type MessengerSend = ( - broker: Broker, - data: Message, -) => Promise | void; - -export type MessengerStop = (broker: Broker) => Promise | void; - -export type AsyncMessengerConfig = Readonly< - Pick< - TypegateConfigBase, - | "timer_max_timeout_ms" - | "timer_destroy_resources" - > ->; - -export class AsyncMessenger { - protected broker: Broker; - #counter = 0; - #tasks: Map = new Map(); - #start: MessengerStart; - #send: MessengerSend; - #stop: MessengerStop; - - #timer?: ReturnType; - - #operationQueues: Array>> = [ - [], - [], - ]; - #queueIndex = 0; - - #timeoutSecs: number; - - protected constructor( - start: MessengerStart, - send: MessengerSend, - stop: MessengerStop, - private config: AsyncMessengerConfig, - ) { - this.#start = start; - this.#send = send; - this.#stop = stop; - this.#timeoutSecs = config.timer_max_timeout_ms / 1000; - // init broker - this.broker = start(this.receive.bind(this)); - this.initTimer(); - } - - async terminate(): Promise { - await Promise.all([...this.#tasks.values()].map((t) => t.promise)); - logger.info(`close worker ${this.constructor.name}`); - await this.#stop(this.broker); - clearInterval(this.#timer); - } - - initTimer() { - if (this.#timer === undefined) { - this.#timer = setInterval(() => { - const currentQueue = this.#operationQueues[this.#queueIndex]; - this.#queueIndex = 1 - this.#queueIndex; - - let shouldStop = false; - for (const item of currentQueue) { - if (this.#tasks.has(item.id)) { - if ( - item.remainingPulseCount !== undefined && - item.remainingPulseCount > 0 - ) { - // check again next time if unterminated - item.remainingPulseCount -= 1; - continue; - } - // default behavior or 0 pulse left - const data = JSON.stringify(item, null, 2); - this.receive({ - id: item.id, - error: `${this.#timeoutSecs}s timeout exceeded: ${data}`, - }); - shouldStop = true; - } - } - - if (shouldStop && this.config.timer_destroy_resources) { - this.#stop(this.broker); - logger.info("reset broker after timeout"); - this.broker = this.#start(this.receive.bind(this)); - } - }, this.config.timer_max_timeout_ms); - } - } - - execute( - op: string | number | null, - data: M, - hooks: Array<() => Promise> = [], - pulseCount = 0, - ): Promise { - const id = this.nextId(); - const promise = Promise.withResolvers(); - this.#tasks.set(id, { promise, hooks }); - - const message = { id, op, data, remainingPulseCount: pulseCount }; - this.#operationQueues[this.#queueIndex].push(message); - void this.#send(this.broker, message); - return promise.promise; - } - - async receive(answer: Answer): Promise { - const { id } = answer; - const { promise, hooks } = this.#tasks.get(id)!; - if (answer.error) { - promise.reject(new Error(answer.error)); - } else { - promise.resolve(answer.data); - } - await Promise.all(hooks.map((h) => h())); - this.#tasks.delete(id); - } - - private nextId(): number { - const n = this.#counter; - this.#counter += 1; - this.#counter %= maxi32; - return n; - } - - get isEmpty(): boolean { - return this.#tasks.size === 0; - } - - get counter(): number { - return this.#counter; - } -} diff --git a/src/typegate/src/runtimes/patterns/messenger/lazy_async_messenger.ts b/src/typegate/src/runtimes/patterns/messenger/lazy_async_messenger.ts deleted file mode 100644 index e645734da..000000000 --- a/src/typegate/src/runtimes/patterns/messenger/lazy_async_messenger.ts +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. -// SPDX-License-Identifier: MPL-2.0 - -import { getLogger } from "../../../log.ts"; -import { maxi32 } from "../../../utils.ts"; -import { - AsyncMessenger, - type AsyncMessengerConfig, - type MessengerSend, - type MessengerStart, - type MessengerStop, -} from "./async_messenger.ts"; - -export type { AsyncMessengerConfig }; - -const logger = getLogger(import.meta); - -const inactivityThreshold = 1; -const inactivityIntervalMs = 15_000; - -export class LazyAsyncMessenger - extends AsyncMessenger { - #gcState = 0; - #gcInterval?: number; - #start: MessengerStart; - - #ops: Map; - #loadedOps: Set = new Set(); - - constructor( - start: MessengerStart, - ops: Map, - send: MessengerSend, - stop: MessengerStop, - config: AsyncMessengerConfig, - ) { - const lazySend: MessengerSend = async ( - _, - message, - ) => { - if (!this.broker) { - this.broker = start( - this.receive.bind(this), - ); - } - const { op, remainingPulseCount } = message; - if (op !== null && !this.#loadedOps.has(op)) { - const initOp = this.#ops.get(op); - if (!initOp) { - throw new Error(`unknown op ${op}`); - } - await this.execute( - null, - initOp, - [], - remainingPulseCount, - ); - this.#loadedOps.add(op); - } - await send(this.broker, message); - }; - - const lazyStop: MessengerStop = async (_) => { - clearInterval(this.#gcInterval); - const broker = this.broker; - if (broker) { - this.broker = null; - this.#loadedOps.clear(); - await stop(broker); - } - }; - - super(() => null, lazySend, lazyStop, config); - this.#start = start; - this.#ops = ops; - } - - enableLazyness(): void { - logger.info(`enable laziness`); - clearInterval(this.#gcInterval); - this.#gcInterval = setInterval( - () => this.checkLazyness(), - inactivityIntervalMs, - ); - } - - async checkLazyness(): Promise { - if (!this.broker) { - return; - } - - const activity = (this.counter - this.#gcState + maxi32) % - maxi32; - this.#gcState = this.counter; - - if (activity <= inactivityThreshold && this.isEmpty) { - logger.info( - `lazy close worker ${this.constructor.name}`, - ); - this.broker = null; - await this.terminate(); - } - } - - disableLazyness(): void { - logger.info(`disable laziness`); - clearInterval(this.#gcInterval); - if (!this.broker) { - this.broker = this.#start( - this.receive.bind(this), - ); - } - } -} diff --git a/src/typegate/src/runtimes/patterns/messenger/types.ts b/src/typegate/src/runtimes/patterns/messenger/types.ts deleted file mode 100644 index 202d67a7c..000000000 --- a/src/typegate/src/runtimes/patterns/messenger/types.ts +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. -// SPDX-License-Identifier: MPL-2.0 - -export interface Message { - id: number; - op: string | number | null; - data: T; - remainingPulseCount?: number; -} - -type PromiseWithResolvers = ReturnType>; - -export type Answer = - | ({ id: number; data: T; error?: never }) - | ({ id: number; data?: never; error: string }); - -export interface TaskData { - promise: PromiseWithResolvers; - hooks: Array<() => void | Promise>; -} diff --git a/src/typegate/src/runtimes/patterns/worker_manager/deno.ts b/src/typegate/src/runtimes/patterns/worker_manager/deno.ts new file mode 100644 index 000000000..b2ec55179 --- /dev/null +++ b/src/typegate/src/runtimes/patterns/worker_manager/deno.ts @@ -0,0 +1,66 @@ +// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +// SPDX-License-Identifier: MPL-2.0 + +import { envSharedWithWorkers } from "../../../config/shared.ts"; +import { BaseWorker } from "./mod.ts"; +import { BaseMessage, EventHandler, TaskId } from "./types.ts"; + +export interface DenoWorkerError extends BaseMessage { + type: "WORKER_ERROR"; + event: ErrorEvent; +} + +export type BaseDenoWorkerMessage = BaseMessage | DenoWorkerError; + +export class DenoWorker + extends BaseWorker { + #worker: Worker; + #taskId: TaskId; + constructor(taskId: TaskId, workerPath: string) { + super(); + this.#worker = new Worker(workerPath, { + name: taskId, + type: "module", + deno: { + permissions: { + net: true, + // on request permissions + read: "inherit", // default read permission + sys: "inherit", + // non-overridable permissions (security between typegraphs) + run: false, + write: false, + ffi: false, + env: envSharedWithWorkers, + }, + }, + }); + this.#taskId = taskId; + } + + listen(handlerFn: EventHandler) { + this.#worker.onmessage = async (message) => { + await handlerFn(message.data as E); + }; + + this.#worker.onerror = /*async*/ (event) => + handlerFn( + { + type: "WORKER_ERROR", + event, + } as E, + ); + } + + send(msg: M) { + this.#worker.postMessage(msg); + } + + destroy() { + this.#worker.terminate(); + } + + get id() { + return this.#taskId; + } +} diff --git a/src/typegate/src/runtimes/patterns/worker_manager/mod.ts b/src/typegate/src/runtimes/patterns/worker_manager/mod.ts new file mode 100644 index 000000000..fe06470cb --- /dev/null +++ b/src/typegate/src/runtimes/patterns/worker_manager/mod.ts @@ -0,0 +1,159 @@ +// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +// SPDX-License-Identifier: MPL-2.0 + +import { getLogger } from "../../../log.ts"; +import { BaseMessage, EventHandler, TaskId } from "./types.ts"; + +const logger = getLogger(import.meta, "WARN"); + +/** + * `M` is the message type that the worker will receive; + * `E` is the message type that the worker will send back (event). + */ +export abstract class BaseWorker { + abstract listen(handlerFn: EventHandler): void; + abstract send(msg: M): void; + abstract destroy(): void; + abstract get id(): TaskId; +} + +export class BaseWorkerManager< + T, + M extends BaseMessage, + E extends BaseMessage, +> { + #activeTasks: Map; + taskSpec: T; + }> = new Map(); + #tasksByName: Map> = new Map(); + #startedAt: Map = new Map(); + + #workerFactory: (taskId: TaskId) => BaseWorker; + protected constructor(workerFactory: (taskId: TaskId) => BaseWorker) { + this.#workerFactory = workerFactory; + } + + get workerFactory() { + return this.#workerFactory; + } + + protected getActiveTaskNames() { + return Array.from(this.#tasksByName.keys()); + } + + protected hasTask(taskId: TaskId) { + return this.#activeTasks.has(taskId); + } + + protected getTask(taskId: TaskId) { + const task = this.#activeTasks.get(taskId); + if (!task) { + throw new Error(`Task "${taskId}" does not exist or has been completed`); + } + return task; + } + + protected getTasksByName(name: string) { + return this.#tasksByName.get(name) ?? new Set(); + } + + getInitialTimeStartedAt(taskId: TaskId) { + const startedAt = this.#startedAt.get(taskId); + if (!startedAt) { + throw new Error( + `Invalid state: cannot find initial time for task "${taskId}"`, + ); + } + return startedAt; + } + + // allocate worker? + protected createWorker(name: string, taskId: TaskId, taskSpec: T) { + const worker = this.#workerFactory(taskId); + // TODO inline + this.addWorker(name, taskId, worker, taskSpec, new Date()); + } + + protected addWorker( + name: string, + taskId: TaskId, + worker: BaseWorker, + taskSpec: T, + startedAt: Date, + ) { + if (!this.#tasksByName.has(name)) { + this.#tasksByName.set(name, new Set()); + } + + this.#tasksByName.get(name)!.add(taskId); + this.#activeTasks.set(taskId, { worker, taskSpec }); + if (!this.#startedAt.has(taskId)) { + this.#startedAt.set(taskId, startedAt); + } + } + + protected destroyAllWorkers() { + for (const name of this.getActiveTaskNames()) { + this.destroyWorkersByName(name); + } + } + + protected destroyWorkersByName(name: string) { + const taskIds = this.#tasksByName.get(name); + if (taskIds) { + for (const taskId of taskIds) { + this.destroyWorker(name, taskId); + } + return true; + } + return false; + } + + protected destroyWorker(name: string, taskId: TaskId) { + const task = this.#activeTasks.get(taskId); + if (this.#tasksByName.has(name)) { + if (!task) { + logger.warn( + `Task "${taskId}" associated with "${name}" does not exist or has been already destroyed`, + ); + return false; + } + + task.worker.destroy(); + this.#activeTasks.delete(taskId); + this.#tasksByName.get(name)!.delete(taskId); + // startedAt records are not deleted + + return true; + } + + return false; + } + + logMessage(_taskId: TaskId, _msg: M) { + // default implementation is empty + } + + sendMessage(taskId: TaskId, msg: M) { + const { worker } = this.getTask(taskId); + worker.send(msg); + this.logMessage(taskId, msg); + } +} + +export function createTaskId(name: string) { + const uuid = crypto.randomUUID(); + const sanitizedName = name.replace(/_::_/g, "__"); + return `${sanitizedName}_::_${uuid}`; +} + +export function getTaskNameFromId(taskId: TaskId) { + const [name, uuid] = taskId.split("_::_"); + if (!name || !uuid) { + // unreachable + throw new Error(`Fatal: task ID ${taskId} does not respect the convention`); + } + + return name; +} diff --git a/src/typegate/src/runtimes/patterns/worker_manager/types.ts b/src/typegate/src/runtimes/patterns/worker_manager/types.ts new file mode 100644 index 000000000..de417a36c --- /dev/null +++ b/src/typegate/src/runtimes/patterns/worker_manager/types.ts @@ -0,0 +1,12 @@ +// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +// SPDX-License-Identifier: MPL-2.0 + +export type TaskId = string; + +export interface BaseMessage { + type: string; +} + +export type EventHandler = ( + message: E, +) => void | Promise; diff --git a/src/typegate/src/runtimes/substantial.ts b/src/typegate/src/runtimes/substantial.ts index 8fada6ea2..336e2372d 100644 --- a/src/typegate/src/runtimes/substantial.ts +++ b/src/typegate/src/runtimes/substantial.ts @@ -20,7 +20,12 @@ import { } from "./substantial/agent.ts"; import { closestWord } from "../utils.ts"; import { InternalAuth } from "../services/auth/protocols/internal.ts"; -import { applyFilter, type Expr, type ExecutionStatus } from "./substantial/filter_utils.ts"; +import { + applyFilter, + type ExecutionStatus, + type Expr, +} from "./substantial/filter_utils.ts"; +import { createTaskId } from "./patterns/worker_manager/mod.ts"; const logger = getLogger(import.meta); @@ -247,7 +252,7 @@ export class SubstantialRuntime extends Runtime { }) => { this.#checkWorkflowExistOrThrow(workflowName); - const runId = Agent.nextId(workflowName); + const runId = createTaskId(workflowName); const schedule = new Date().toJSON(); const token = await InternalAuth.emit(this.typegate.cryptoKeys); diff --git a/src/typegate/src/runtimes/substantial/agent.ts b/src/typegate/src/runtimes/substantial/agent.ts index 3e2691ca7..fe54fccc5 100644 --- a/src/typegate/src/runtimes/substantial/agent.ts +++ b/src/typegate/src/runtimes/substantial/agent.ts @@ -10,14 +10,15 @@ import { } from "../../../engine/runtime.js"; import { getLoggerByAddress, Logger } from "../../log.ts"; import { TaskContext } from "../deno/shared_types.ts"; +import { getTaskNameFromId } from "../patterns/worker_manager/mod.ts"; +import { EventHandler } from "../patterns/worker_manager/types.ts"; import { appendIfOngoing, - Interrupt, - Result, - WorkerData, - WorkflowResult, + InterruptEvent, + WorkflowCompletionEvent, + WorkflowEvent, } from "./types.ts"; -import { RunId, WorkerManager } from "./workflow_worker_manager.ts"; +import { WorkerManager } from "./workflow_worker_manager.ts"; export interface StdKwargs { taskContext: TaskContext; @@ -45,7 +46,7 @@ export class Agent { constructor( private backend: Backend, private queue: string, - private config: AgentConfig + private config: AgentConfig, ) { this.logger = getLoggerByAddress(import.meta, "substantial"); } @@ -64,7 +65,7 @@ export class Agent { }); } catch (err) { this.logger.warn( - `Failed writing log metadata for schedule "${schedule}" (${runId}), skipping it: ${err}` + `Failed writing log metadata for schedule "${schedule}" (${runId}), skipping it: ${err}`, ); } } @@ -97,9 +98,11 @@ export class Agent { this.workflows = workflows; this.logger.warn( - `Initializing agent to handle ${workflows - .map(({ name }) => name) - .join(", ")}` + `Initializing agent to handle ${ + workflows + .map(({ name }) => name) + .join(", ") + }`, ); this.pollIntervalHandle = setInterval(async () => { @@ -138,7 +141,7 @@ export class Agent { for (const workflow of this.workflows) { const requests = replayRequests.filter( - ({ run_id }) => Agent.parseWorkflowName(run_id) == workflow.name + ({ run_id }) => getTaskNameFromId(run_id) == workflow.name, ); while (requests.length > 0) { @@ -149,7 +152,7 @@ export class Agent { await this.#replay(next, workflow); } catch (err) { this.logger.error( - `Replay failed for ${workflow.name} => ${JSON.stringify(next)}` + `Replay failed for ${workflow.name} => ${JSON.stringify(next)}`, ); this.logger.error(err); } finally { @@ -195,7 +198,7 @@ export class Agent { // necessarily represent the state of what is actually running on the current typegate node if (this.workerManager.isOngoing(next.run_id)) { this.logger.warn( - `skip triggering ${next.run_id} for the current tick as it is still ongoing` + `skip triggering ${next.run_id} for the current tick as it is still ongoing`, ); return; @@ -222,7 +225,7 @@ export class Agent { // This may occur if an event is sent but the underlying run already completed // Or does not exist. this.logger.warn( - `Run ${next.run_id} has already stopped, closing schedule` + `Run ${next.run_id} has already stopped, closing schedule`, ); await Meta.substantial.storeCloseSchedule(schedDef); return; @@ -242,9 +245,11 @@ export class Agent { // A consequence of the above, a workflow is always triggered by gql { start(..) } // This can also occur if an event is sent from gql under a runId that is not valid (e.g. due to typo) this.logger.warn( - `First item in the operation list is not a Start, got "${JSON.stringify( - first - )}" instead. Closing the underlying schedule.` + `First item in the operation list is not a Start, got "${ + JSON.stringify( + first, + ) + }" instead. Closing the underlying schedule.`, ); await Meta.substantial.storeCloseSchedule(schedDef); @@ -259,12 +264,12 @@ export class Agent { workflow.path, run, next.schedule_date, - taskContext + taskContext, ); this.workerManager.listen( next.run_id, - this.#eventResultHandlerFor(workflow.name, next.run_id) + this.#eventResultHandlerFor(workflow.name, next.run_id), ); } catch (err) { throw err; @@ -281,73 +286,52 @@ export class Agent { } } - #eventResultHandlerFor(workflowName: string, runId: string) { - return async (result: Result) => { - if (result.error) { - // All Worker/Runner non-user issue should fall here - // Note: Should never throw (typegate will panic), this will run in a worker - this.logger.error( - `result error for "${runId}": ${JSON.stringify(result.payload)}` - ); - return; - } - - const answer = result.payload as WorkerData; - this.logger.info( - `"${runId}" answered: type ${JSON.stringify(answer.type)}` - ); - + #eventResultHandlerFor( + workflowName: string, + runId: string, + ): EventHandler { + return async (e) => { const startedAt = this.workerManager.getInitialTimeStartedAt(runId); - switch (answer.type) { - case "START": { - const ret = answer.data as WorkflowResult; - const interrupt = Interrupt.getTypeOf(ret.exception); - switch (interrupt) { - case "SAVE_RETRY": - case "SLEEP": - case "WAIT_ENSURE_VALUE": - case "WAIT_HANDLE_EVENT": - case "WAIT_RECEIVE_EVENT": { - await this.#workflowHandleInterrupts(workflowName, runId, ret); - break; - } - case null: { - await this.#workflowHandleGracefullCompletion( - startedAt, - workflowName, - runId, - ret - ); - break; - } - default: - throw new Error(`Unknown interrupt "${interrupt}"`); - } + switch (e.type) { + case "SUCCESS": + case "FAIL": + await this.#workflowHandleGracefullCompletion( + startedAt, + workflowName, + runId, + e, + ); break; - } - default: + case "ERROR": this.logger.error( - `Fatal: invalid type ${ - answer.type - } sent by "${runId}": ${JSON.stringify(answer.data)}` + `Result error for "${runId}": ${JSON.stringify(e.error)}`, ); + return; + case "INTERRUPT": + // TODO unknown interrupt + await this.#workflowHandleInterrupts(workflowName, runId, e); + break; } + + // this.logger.info( + // `"${runId}" answered: type ${JSON.stringify(answer.type)}`, + // ); }; } async #workflowHandleInterrupts( workflowName: string, runId: string, - { result, schedule, run }: WorkflowResult + { interrupt, schedule, run }: InterruptEvent, ) { this.workerManager.destroyWorker(workflowName, runId); // ! - this.logger.debug(`Interrupt "${workflowName}": ${result}"`); + this.logger.debug(`Interrupt "${workflowName}": ${interrupt}"`); // TODO: make all of these transactional - this.logger.info(`Persist records for "${workflowName}": ${result}"`); + this.logger.info(`Persist records for "${workflowName}": ${interrupt}"`); const _run = await Meta.substantial.storePersistRun({ backend: this.backend, run, @@ -388,38 +372,41 @@ export class Agent { startedAt: Date, workflowName: string, runId: string, - { result, kind, schedule, run }: WorkflowResult + event: WorkflowCompletionEvent, ) { this.workerManager.destroyWorker(workflowName, runId); + console.log({ event }); + + const result = event.type == "SUCCESS" ? event.result : event.error; this.logger.info( - `gracefull completion of "${runId}" (${kind}): ${JSON.stringify( - result - )} started at "${startedAt}"` + `gracefull completion of "${runId}" (${event.type}): ${ + JSON.stringify(result) + } started at "${startedAt}"`, ); this.logger.info(`Append Stop ${runId}`); - const rustResult = kind == "FAIL" ? "Err" : "Ok"; + const rustResult = event.type == "FAIL" ? "Err" : "Ok"; // Note: run is a one-time value, thus can be mutated - appendIfOngoing(run, { + appendIfOngoing(event.run, { at: new Date().toJSON(), event: { type: "Stop", result: { - [rustResult]: result ?? null, + [rustResult]: result, } as unknown, }, }); this.logger.info( - `Persist finalized records for "${workflowName}": ${result}" and closing everything..` + `Persist finalized records for "${workflowName}": ${result}" and closing everything..`, ); const _run = await Meta.substantial.storePersistRun({ backend: this.backend, - run, + run: event.run, }); // console.log("Persisted", run); @@ -428,7 +415,7 @@ export class Agent { backend: this.backend, queue: this.queue, run_id: runId, - schedule, + schedule: event.schedule, }); await Meta.substantial.agentRemoveLease({ @@ -437,21 +424,6 @@ export class Agent { lease_seconds: this.config.leaseLifespanSec, }); } - - static nextId(name: string): RunId { - const uuid = crypto.randomUUID(); - return `${name}_::_${uuid}`; - } - - static parseWorkflowName(runId: string) { - const [name, uuid] = runId.split("_::_"); - if (!name && !uuid) { - // Impossible since it must be produced from nextId upon a Start event - throw new Error(`Fatal: ${runId} does not respect the convention`); - } - - return name; - } } function checkIfRunHasStopped(run: Run) { @@ -464,13 +436,15 @@ function checkIfRunHasStopped(run: Run) { if (op.event.type == "Start") { if (life >= 1) { logger.error( - `bad logs: ${JSON.stringify( - run.operations.map(({ event }) => event.type) - )}` + `bad logs: ${ + JSON.stringify( + run.operations.map(({ event }) => event.type), + ) + }`, ); throw new Error( - `"${run.run_id}" has potentially corrupted logs, another run occured yet previous has not stopped` + `"${run.run_id}" has potentially corrupted logs, another run occured yet previous has not stopped`, ); } @@ -479,13 +453,15 @@ function checkIfRunHasStopped(run: Run) { } else if (op.event.type == "Stop") { if (life <= 0) { logger.error( - `bad logs: ${JSON.stringify( - run.operations.map(({ event }) => event.type) - )}` + `bad logs: ${ + JSON.stringify( + run.operations.map(({ event }) => event.type), + ) + }`, ); throw new Error( - `"${run.run_id}" has potentitally corrupted logs, attempted stopping already closed run, or run with a missing Start` + `"${run.run_id}" has potentitally corrupted logs, attempted stopping already closed run, or run with a missing Start`, ); } diff --git a/src/typegate/src/runtimes/substantial/types.ts b/src/typegate/src/runtimes/substantial/types.ts index c750130e6..5d38d05ef 100644 --- a/src/typegate/src/runtimes/substantial/types.ts +++ b/src/typegate/src/runtimes/substantial/types.ts @@ -2,6 +2,8 @@ // SPDX-License-Identifier: MPL-2.0 import { Operation, Run } from "../../../engine/runtime.js"; +import { TaskContext } from "../deno/shared_types.ts"; +import { DenoWorkerError } from "../patterns/worker_manager/deno.ts"; export type { Backend, Operation, @@ -9,44 +11,55 @@ export type { Run, } from "../../../engine/runtime.js"; -export type AnyString = string & Record; - -export type WorkerEvent = "START" | AnyString; +export type WorkflowMessage = { + type: "START"; + data: { + modulePath: string; + functionName: string; + run: Run; + schedule: string; + internal: TaskContext; + }; +}; -export type WorkerData = { - type: WorkerEvent; - data: any; +export type WorkflowCompletionEvent = + | { + type: "SUCCESS"; + result: unknown; + run: Run; + schedule: string; + } + | { + type: "FAIL"; + error: string; + exception: Error | undefined; + run: Run; + schedule: string; + }; + +export type InterruptEvent = { + type: "INTERRUPT"; + interrupt: InterruptType; + schedule: string; + run: Run; }; -export type WorkerEventHandler = (message: Result) => Promise; +export type WorkflowEvent = + | WorkflowCompletionEvent + | InterruptEvent + | { + type: "ERROR"; + error: string; + } + | DenoWorkerError; export type Result = { error: boolean; payload: T; }; -export function Ok(payload: R): Result { - return { error: false, payload }; -} - -export function Err(payload: E): Result { - return { error: true, payload }; -} - -export function Msg(type: WorkerEvent, data: unknown): WorkerData { - return { type, data }; -} - export type ExecutionResultKind = "SUCCESS" | "FAIL"; -export type WorkflowResult = { - kind: ExecutionResultKind; - result: unknown; - exception?: Error; - schedule: string; - run: Run; -}; - // TODO: convert python exceptions into these // by using prefixes on the exception message for example diff --git a/src/typegate/src/runtimes/substantial/worker.ts b/src/typegate/src/runtimes/substantial/worker.ts index ebc0505dc..4de7df8b0 100644 --- a/src/typegate/src/runtimes/substantial/worker.ts +++ b/src/typegate/src/runtimes/substantial/worker.ts @@ -4,12 +4,12 @@ import { errorToString } from "../../worker_utils.ts"; import { Context } from "./deno_context.ts"; import { toFileUrl } from "@std/path/to-file-url"; -import { Err, Msg, Ok, WorkerData, WorkflowResult } from "./types.ts"; +import { Interrupt, WorkflowEvent, WorkflowMessage } from "./types.ts"; let runCtx: Context | undefined; self.onmessage = async function (event) { - const { type, data } = event.data as WorkerData; + const { type, data } = event.data as WorkflowMessage; switch (type) { case "START": { const { modulePath, functionName, run, schedule, internal } = data; @@ -21,7 +21,12 @@ self.onmessage = async function (event) { const workflowFn = module[functionName]; if (typeof workflowFn !== "function") { - self.postMessage(Err(`Function "${functionName}" not found`)); + self.postMessage( + { + type: "ERROR", + error: `Function "${functionName}" not found`, + } satisfies WorkflowEvent, + ); self.close(); return; } @@ -31,40 +36,48 @@ self.onmessage = async function (event) { workflowFn(runCtx, internal) .then((wfResult: unknown) => { self.postMessage( - Ok( - Msg( - type, - { - kind: "SUCCESS", - result: wfResult, - run: runCtx!.getRun(), - schedule, - } satisfies WorkflowResult, - ), - ), + { + type: "SUCCESS", + result: wfResult, + run: runCtx!.getRun(), + schedule, + } satisfies WorkflowEvent, ); }) .catch((wfException: unknown) => { - self.postMessage( - Ok( - Msg( - type, - { - kind: "FAIL", - result: errorToString(wfException), - exception: wfException instanceof Error - ? wfException - : undefined, - run: runCtx!.getRun(), - schedule, - } satisfies WorkflowResult, - ), - ), - ); + const interrupt = Interrupt.getTypeOf(wfException); + if (interrupt) { + self.postMessage( + { + type: "INTERRUPT", + interrupt, + run: runCtx!.getRun(), + schedule, + } satisfies WorkflowEvent, + ); + } else { + self.postMessage( + { + type: "FAIL", + error: errorToString(wfException), + // How?? + exception: wfException instanceof Error + ? wfException + : undefined, + run: runCtx!.getRun(), + schedule, + } satisfies WorkflowEvent, + ); + } }); break; } default: - self.postMessage(Err(Msg(type, `Unknown command ${type}`))); + self.postMessage( + { + type: "ERROR", + error: `Unknown command ${type}`, + } satisfies WorkflowEvent, + ); } }; diff --git a/src/typegate/src/runtimes/substantial/workflow_worker_manager.ts b/src/typegate/src/runtimes/substantial/workflow_worker_manager.ts index c7223d11f..d6176dc30 100644 --- a/src/typegate/src/runtimes/substantial/workflow_worker_manager.ts +++ b/src/typegate/src/runtimes/substantial/workflow_worker_manager.ts @@ -1,169 +1,54 @@ // Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. // SPDX-License-Identifier: MPL-2.0 -import { envSharedWithWorkers } from "../../config/shared.ts"; import { getLogger } from "../../log.ts"; import { TaskContext } from "../deno/shared_types.ts"; -import { - Err, - Msg, - Result, - Run, - WorkerEvent, - WorkerEventHandler, -} from "./types.ts"; +import { DenoWorker } from "../patterns/worker_manager/deno.ts"; +import { BaseWorkerManager } from "../patterns/worker_manager/mod.ts"; +import { EventHandler, TaskId } from "../patterns/worker_manager/types.ts"; +import { Run, WorkflowEvent, WorkflowMessage } from "./types.ts"; const logger = getLogger(import.meta, "WARN"); -export type WorkerRecord = { - worker: Worker; +export type WorkflowSpec = { modulePath: string; }; -export type RunId = string; -export type WorkflowName = string; - -export class WorkflowRecorder { - workflowRuns: Map> = new Map(); - workers: Map = new Map(); - startedAtRecords: Map = new Map(); - - getRegisteredWorkflowNames() { - return Array.from(this.workflowRuns.keys()); - } - - hasRun(runId: RunId) { - return this.workers.has(runId); - } - - getWorkerRecord(runId: RunId) { - const record = this.workers.get(runId); - if (!record) { - throw new Error(`Run "${runId}" does not exist or has been completed`); - } - - return record!; - } - - addWorker( - name: WorkflowName, - runId: RunId, - worker: WorkerRecord, - startedAt: Date, - ) { - if (!this.workflowRuns.has(name)) { - this.workflowRuns.set(name, new Set()); - } - - this.workflowRuns.get(name)!.add(runId); - this.workers.set(runId, worker); - if (!this.startedAtRecords.has(runId)) { - this.startedAtRecords.set(runId, startedAt); - } - } - - destroyAllWorkers() { - for (const name of this.getRegisteredWorkflowNames()) { - this.destroyRelatedWorkers(name); - } - } - - destroyRelatedWorkers(name: WorkflowName) { - if (this.workflowRuns.has(name)) { - const runIds = this.workflowRuns.get(name)!.values(); - for (const runId of runIds) { - this.destroyWorker(name, runId); - } - return true; - } - return false; - } - - destroyWorker(name: WorkflowName, runId: RunId) { - const record = this.workers.get(runId); - if (this.workflowRuns.has(name)) { - if (!record) { - logger.warn( - `"${runId}" associated with "${name}" does not exist or has been already destroyed`, - ); - return false; - } - - record!.worker.terminate(); // ! - - this.workflowRuns.get(name)!.delete(runId); - this.workers.delete(runId); - - // Let it alive throughout typegate lifetime - // x this.startedAtRecords.delete(runId); - return true; - } - - return false; - } -} /** * - A workflow file can contain multiple workflows (functions) * - A workflow can be run as many times as a START event is triggered (with a run_id) * - The completion of a workflow is run async, it is entirely up to the event listeners to act upon the results */ -export class WorkerManager { - private recorder: WorkflowRecorder = new WorkflowRecorder(); - - constructor() {} - - #createWorker(name: string, modulePath: string, runId: RunId) { - const worker = new Worker(import.meta.resolve("./worker.ts"), { - name: runId, - type: "module", - deno: { - permissions: { - net: true, - // on request permissions - read: "inherit", // default read permission - sys: "inherit", - // non-overridable permissions (security between typegraphs) - run: false, - write: false, - ffi: false, - env: envSharedWithWorkers, - }, - }, +export class WorkerManager + extends BaseWorkerManager { + constructor() { + super((taskId: TaskId) => { + return new DenoWorker(taskId, import.meta.resolve("./worker.ts")); }); - - this.recorder.addWorker( - name, - runId, - { - modulePath, - worker, - }, - new Date(), - ); } destroyWorker(name: string, runId: string) { - return this.recorder.destroyWorker(name, runId); + return super.destroyWorker(name, runId); } destroyAllWorkers() { - this.recorder.destroyAllWorkers(); logger.warn( - `Destroyed workers for ${ - this.recorder - .getRegisteredWorkflowNames() + `Destroying workers for ${ + this + .getActiveTaskNames() .map((w) => `"${w}"`) .join(", ") }`, ); + super.destroyAllWorkers(); } - isOngoing(runId: RunId) { - return this.recorder.hasRun(runId); + isOngoing(runId: TaskId) { + return this.hasTask(runId); } - getAllocatedResources(name: WorkflowName) { - const runIds = this.recorder.workflowRuns.get(name) ?? new Set(); + getAllocatedResources(name: string) { + const runIds = super.getTasksByName(name) ?? new Set(); return { count: runIds.size, workflow: name, @@ -176,43 +61,20 @@ export class WorkerManager { }; } - /** Returns a Date object representing the *initial* time the runId has been registered/run */ - getInitialTimeStartedAt(runId: RunId): Date { - const rec = this.recorder.startedAtRecords.get(runId); - if (!rec) { - throw new Error( - `Invalid state: cannot find initial time for run "${runId}"`, - ); - } - return rec; - } - - listen(runId: RunId, handlerFn: WorkerEventHandler) { - if (!this.recorder.hasRun(runId)) { + listen(runId: TaskId, handlerFn: EventHandler) { + if (!this.hasTask(runId)) { // Note: never throw on worker events, this will make typegate panic! logger.warn(`Attempt listening on missing ${runId}`); return; } - const { worker } = this.recorder.getWorkerRecord(runId); - - worker.onmessage = async (message) => { - if (message.data.error) { - // worker level failure - await handlerFn(Err(message.data.error)); - } else { - // logic level Result (Ok | Err) - await handlerFn(message.data as Result); - } - }; + const { worker } = this.getTask(runId); - worker.onerror = /*async*/ (event) => handlerFn(Err(event)); + worker.listen(handlerFn); } - trigger(type: WorkerEvent, runId: RunId, data: unknown) { - const { worker } = this.recorder.getWorkerRecord(runId); - worker.postMessage(Msg(type, data)); - logger.info(`trigger ${type} for ${runId}`); + override logMessage(runId: TaskId, msg: WorkflowMessage) { + logger.info(`trigger ${msg.type} for ${runId}`); } triggerStart( @@ -223,13 +85,18 @@ export class WorkerManager { schedule: string, internalTCtx: TaskContext, ) { - this.#createWorker(name, workflowModPath, runId); - this.trigger("START", runId, { + this.createWorker(name, runId, { modulePath: workflowModPath, - functionName: name, - run: storedRun, - schedule, - internal: internalTCtx, + }); + this.sendMessage(runId, { + type: "START", + data: { + modulePath: workflowModPath, + functionName: name, + run: storedRun, + schedule, + internal: internalTCtx, + }, }); } } diff --git a/src/typegate/src/runtimes/typegate.ts b/src/typegate/src/runtimes/typegate.ts index 8d1a08703..76fd4638b 100644 --- a/src/typegate/src/runtimes/typegate.ts +++ b/src/typegate/src/runtimes/typegate.ts @@ -114,7 +114,8 @@ export class TypeGateRuntime extends Runtime { return this.execRawPrismaQuery; case "queryPrismaModel": return this.queryPrismaModel; - + case "ping": + return (_) => true; default: if (name != null) { throw new Error(`materializer '${name}' not implemented`); diff --git a/src/typegate/src/sync/replicated_map.ts b/src/typegate/src/sync/replicated_map.ts index b7317cb9c..cba6b3ffe 100644 --- a/src/typegate/src/sync/replicated_map.ts +++ b/src/typegate/src/sync/replicated_map.ts @@ -105,27 +105,43 @@ export class RedisReplicatedMap implements AsyncDisposable { this.redisObs.close(); } + async getAllHistory() { + const { key, redis } = this; + const all = await redis.hgetall(key); + const history = []; + for (let i = 0; i < all.length; i += 2) { + history.push({ + name: all[i], + payload: all[i+1] + }); + } + + return history; + } + async historySync(): Promise { - const { key, redis, deserializer } = this; + const { redis, deserializer } = this; // get last received message before loading history const [lastMessage] = await redis.xrevrange(this.ekey, "+", "-", 1); const lastId = lastMessage ? lastMessage.xid : 0; logger.debug("last message loaded: {}", lastId); - const all = await redis.hgetall(key); + const all = await this.getAllHistory(); logger.debug("history load start: {} elements", all.length); - for (let i = 0; i < all.length; i += 2) { - const name = all[i]; - const payload = all[i + 1]; + + for (const { name, payload } of all) { logger.info(`reloaded addition: ${name}`); ensure( !this.memory.has(name), () => `typegraph ${name} should not exists in memory at first sync`, ); - this.memory.set(name, await deserializer(payload, true)); + + const engine = await deserializer(payload, true); + this.memory.set(name, engine); } logger.debug("history load end"); + return lastId; } diff --git a/src/typegate/src/typegate/artifacts/mod.ts b/src/typegate/src/typegate/artifacts/mod.ts index 34fb655da..9138a7952 100644 --- a/src/typegate/src/typegate/artifacts/mod.ts +++ b/src/typegate/src/typegate/artifacts/mod.ts @@ -195,6 +195,27 @@ export class ArtifactStore implements AsyncDisposable { return this.#resolveLocalPath(meta, parentDirName); } + async getInlineArtifact( + tgName: string, + code: string, + ext: string, + transform = (code: string) => code, + ) { + const hash = await sha256(code); + const path = resolve( + this.persistence.dirs.cache, + "inline", + tgName, + hash + ext, + ); + if (await exists(path)) { + return path; + } + await Deno.mkdir(dirname(path), { recursive: true }); + await Deno.writeTextFile(path, transform(code)); + return path; + } + prepareUpload(meta: ArtifactMeta) { return this.uploadEndpoints.prepareUpload(meta, this.persistence); } diff --git a/src/typegate/src/typegate/mod.ts b/src/typegate/src/typegate/mod.ts index 8760da002..ab34eb962 100644 --- a/src/typegate/src/typegate/mod.ts +++ b/src/typegate/src/typegate/mod.ts @@ -43,7 +43,7 @@ import type { ArtifactStore } from "./artifacts/mod.ts"; // TODO move from tests (MET-497) import { MemoryRegister } from "./memory_register.ts"; import { NoLimiter } from "./no_limiter.ts"; -import { TypegraphStore } from "../sync/typegraph.ts"; +import { typegraphIdSchema, TypegraphStore } from "../sync/typegraph.ts"; import { createLocalArtifactStore } from "./artifacts/local.ts"; import { createSharedArtifactStore } from "./artifacts/shared.ts"; import { AsyncDisposableStack } from "dispose"; @@ -141,15 +141,30 @@ export class Typegate implements AsyncDisposable { stack.move(), ); + const typegraphStore = TypegraphStore.init(syncConfig, cryptoKeys); const register = await ReplicatedRegister.init( typegate, syncConfig.redis, - TypegraphStore.init(syncConfig, cryptoKeys), + typegraphStore ); typegate.disposables.use(register); (typegate as { register: Register }).register = register; + + if (config.sync?.forceRemove) { + logger.warn("Force removal at boot enabled"); + const history = await register.replicatedMap.getAllHistory(); + for (const { name, payload } of history) { + try { + await typegate.forceRemove(name, payload, typegraphStore); + } catch (e) { + logger.error(`Failed to force remove typegraph "${name}": ${e}`); + Sentry.captureException(e); + } + } + } + const lastSync = await register.historySync().catch((err) => { logger.error(err); throw new Error( @@ -397,6 +412,22 @@ export class Typegate implements AsyncDisposable { await this.artifactStore.runArtifactGC(); } + async forceRemove(name: string, payload: string, typegraphStore: TypegraphStore) { + logger.warn(`Dropping "${name}": started`); + const typegraphId = typegraphIdSchema.parse(JSON.parse(payload)); + const [tg] = await typegraphStore.download( + typegraphId, + ); + const artifacts = new Set( + Object.values(tg.meta.artifacts).map((m) => m.hash), + ); + + await this.register.remove(name); + await this.artifactStore.updateRefCounts(new Set(), artifacts); + await this.artifactStore.runArtifactGC(); + logger.warn(`Dropping "${name}": done`); + } + async initQueryEngine( tgDS: TypeGraphDS, secretManager: SecretManager, diff --git a/src/typegate/src/typegate/register.ts b/src/typegate/src/typegate/register.ts index 42d3ebbdc..c3f88befb 100644 --- a/src/typegate/src/typegate/register.ts +++ b/src/typegate/src/typegate/register.ts @@ -86,7 +86,7 @@ export class ReplicatedRegister extends Register { return new ReplicatedRegister(replicatedMap); } - constructor(private replicatedMap: RedisReplicatedMap) { + constructor(public replicatedMap: RedisReplicatedMap) { super(); } diff --git a/src/typegate/src/typegraphs/typegate.json b/src/typegate/src/typegraphs/typegate.json index 828df86a1..16d4e7716 100644 --- a/src/typegate/src/typegraphs/typegate.json +++ b/src/typegate/src/typegraphs/typegate.json @@ -15,7 +15,8 @@ "execRawPrismaCreate": 64, "execRawPrismaUpdate": 65, "execRawPrismaDelete": 66, - "queryPrismaModel": 67 + "queryPrismaModel": 67, + "ping": 71 }, "id": [], "required": [ @@ -30,7 +31,8 @@ "execRawPrismaCreate", "execRawPrismaUpdate", "execRawPrismaDelete", - "queryPrismaModel" + "queryPrismaModel", + "ping" ], "policies": { "typegraphs": [ @@ -68,6 +70,9 @@ ], "queryPrismaModel": [ 0 + ], + "ping": [ + 0 ] } }, @@ -83,7 +88,7 @@ }, { "type": "object", - "title": "root_typegraphs_fn_input", + "title": "struct_542d7", "properties": {}, "id": [], "required": [] @@ -109,7 +114,7 @@ }, { "type": "string", - "title": "Typegraph_name_string" + "title": "string_5d176" }, { "type": "string", @@ -198,7 +203,7 @@ }, { "type": "string", - "title": "root_addTypegraph_fn_input_fromString_string_json", + "title": "string_json_df54e", "format": "json" }, { @@ -268,7 +273,7 @@ }, { "type": "optional", - "title": "root_addTypegraph_fn_output_failure_root_addTypegraph_fn_input_fromString_string_json_optional", + "title": "root_addTypegraph_fn_output_failure_string_json_df54e_optional", "item": 14, "default_value": null }, @@ -296,12 +301,12 @@ }, { "type": "list", - "title": "root_removeTypegraphs_fn_input_names_Typegraph_name_string_list", + "title": "string_5d176_list_691ee", "items": 5 }, { "type": "boolean", - "title": "root_removeTypegraphs_fn_output" + "title": "boolean_a56cd" }, { "type": "function", @@ -333,7 +338,7 @@ }, { "type": "list", - "title": "root_argInfoByPath_fn_input_argPaths_root_removeTypegraphs_fn_input_names_Typegraph_name_string_list_list", + "title": "root_argInfoByPath_fn_input_argPaths_string_5d176_list_691ee_list", "items": 24 }, { @@ -369,18 +374,18 @@ }, { "type": "optional", - "title": "TypeInfo_enum_TypeInfo_enum_root_addTypegraph_fn_input_fromString_string_json_list_optional", + "title": "TypeInfo_enum_TypeInfo_enum_string_json_df54e_list_optional", "item": 32, "default_value": null }, { "type": "list", - "title": "TypeInfo_enum_root_addTypegraph_fn_input_fromString_string_json_list", + "title": "TypeInfo_enum_string_json_df54e_list", "items": 14 }, { "type": "optional", - "title": "TypeInfo_format_Typegraph_name_string_optional", + "title": "TypeInfo_format_string_5d176_optional", "item": 5, "default_value": null }, @@ -440,7 +445,7 @@ }, { "type": "object", - "title": "root_findAvailableOperations_fn_input", + "title": "struct_8f8a3", "properties": { "typegraph": 5 }, @@ -596,7 +601,7 @@ }, { "type": "object", - "title": "root_execRawPrismaRead_fn_input", + "title": "struct_5fa3d", "properties": { "typegraph": 5, "runtime": 5, @@ -777,7 +782,7 @@ }, { "type": "integer", - "title": "root_queryPrismaModel_fn_input_offset_integer" + "title": "integer_e116e" }, { "type": "object", @@ -794,6 +799,16 @@ "rowCount": [], "data": [] } + }, + { + "type": "function", + "title": "root_ping_fn", + "input": 2, + "output": 25, + "runtimeConfig": null, + "materializer": 14, + "rate_weight": null, + "rate_calls": false } ], "materializers": [ @@ -807,15 +822,21 @@ "data": {} }, { - "name": "function", + "name": "predefined_function", "runtime": 0, "effect": { "effect": "read", "idempotent": true }, "data": { - "script": "var _my_lambda = (_args, { context }) => context.username === 'admin' ? 'ALLOW' : 'DENY' ", - "secrets": [] + "name": "context_check", + "param": { + "key": "username", + "value": { + "type": "value", + "value": "admin" + } + } } }, { @@ -925,6 +946,15 @@ "idempotent": true }, "data": {} + }, + { + "name": "ping", + "runtime": 1, + "effect": { + "effect": "read", + "idempotent": true + }, + "data": {} } ], "runtimes": [ @@ -942,7 +972,7 @@ ], "policies": [ { - "name": "admin_only", + "name": "__ctx_username_admin", "materializer": 1 } ], diff --git a/src/typegate/src/typegraphs/typegate.py b/src/typegate/src/typegraphs/typegate.py index 5008c472e..7c74df1bb 100644 --- a/src/typegate/src/typegraphs/typegate.py +++ b/src/typegate/src/typegraphs/typegate.py @@ -1,12 +1,11 @@ # Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. # SPDX-License-Identifier: MPL-2.0 -from typegraph import Graph, fx, t, typegraph +from typegraph import Graph, fx, t, typegraph, Policy from typegraph.gen.exports.runtimes import TypegateOperation from typegraph.gen.types import Err from typegraph.graph.params import Auth, Cors, Rate from typegraph.runtimes.base import Materializer -from typegraph.runtimes.deno import DenoRuntime from typegraph.wit import runtimes, store ### Prisma query (Json protocol): @@ -95,11 +94,7 @@ ), ) def typegate(g: Graph): - deno = DenoRuntime() - admin_only = deno.policy( - "admin_only", - code="(_args, { context }) => context.username === 'admin' ? 'ALLOW' : 'DENY' ", - ) + admin_only = Policy.context("username", "admin") g.auth(Auth.basic(["admin"])) @@ -151,6 +146,11 @@ def typegate(g: Graph): raise Exception(arg_info_by_path_id.value) arg_info_by_path_mat = Materializer(arg_info_by_path_id.value, effect=fx.read()) + ping_mat_id = runtimes.register_typegate_materializer(store, TypegateOperation.PING) + if isinstance(ping_mat_id, Err): + raise Exception(ping_mat_id.value) + ping_mat = Materializer(ping_mat_id.value, effect=fx.read()) + serialized = t.gen(t.string(), serialized_typegraph_mat) typegraph = t.struct( @@ -424,4 +424,9 @@ def typegate(g: Graph): raw_prisma_delete_mat, ), queryPrismaModel=query_prisma_model, + ping=t.func( + t.struct({}), + t.boolean(), # always True + ping_mat, + ), ) diff --git a/src/typegraph/core/src/conversion/runtimes.rs b/src/typegraph/core/src/conversion/runtimes.rs index 76c7dc89e..537bd4e7d 100644 --- a/src/typegraph/core/src/conversion/runtimes.rs +++ b/src/typegraph/core/src/conversion/runtimes.rs @@ -128,10 +128,7 @@ impl MaterializerConverter for DenoMaterializer { ("import_function".to_string(), data) } Predefined(predef) => { - let data = serde_json::from_value(json!({ - "name": predef.name, - })) - .unwrap(); + let data = serde_json::from_value(serde_json::to_value(predef).unwrap()).unwrap(); ("predefined_function".to_string(), data) } }; diff --git a/src/typegraph/core/src/errors.rs b/src/typegraph/core/src/errors.rs index ef33a1421..0f8804b1f 100644 --- a/src/typegraph/core/src/errors.rs +++ b/src/typegraph/core/src/errors.rs @@ -143,10 +143,6 @@ pub fn object_not_found(kind: &str, id: u32) -> TgError { // .into() // } -pub fn unknown_predefined_function(name: &str, runtime: &str) -> TgError { - format!("unknown predefined function {name} for runtime {runtime}").into() -} - pub fn duplicate_policy_name(name: &str) -> TgError { format!("duplicate policy name '{name}'").into() } diff --git a/src/typegraph/core/src/global_store.rs b/src/typegraph/core/src/global_store.rs index b09b4a3a6..305ccdf3d 100644 --- a/src/typegraph/core/src/global_store.rs +++ b/src/typegraph/core/src/global_store.rs @@ -14,6 +14,7 @@ use crate::wit::utils::Auth as WitAuth; #[allow(unused)] use crate::wit::runtimes::{Effect, MaterializerDenoPredefined, MaterializerId}; +use common::typegraph::runtimes::deno::PredefinedFunctionMatData; use graphql_parser::parse_query; use indexmap::IndexMap; use std::rc::Rc; @@ -55,7 +56,7 @@ pub struct Store { pub policies: Vec, deno_runtime: RuntimeId, - predefined_deno_functions: HashMap, + predefined_deno_functions: HashMap, deno_modules: IndexMap, public_policy_id: PolicyId, @@ -88,9 +89,7 @@ impl Store { runtime_id: deno_runtime, effect: Effect::Read, data: MaterializerData::Deno(Rc::new(DenoMaterializer::Predefined( - crate::wit::runtimes::MaterializerDenoPredefined { - name: "pass".to_string(), - }, + PredefinedFunctionMatData::Pass, ))), }], @@ -104,8 +103,6 @@ impl Store { } } -const PREDEFINED_DENO_FUNCTIONS: &[&str] = &["identity", "true"]; - thread_local! { pub static STORE: RefCell = RefCell::new(Store::new()); pub static SDK_VERSION: String = "0.5.0-rc.9".to_owned(); @@ -350,25 +347,24 @@ impl Store { with_store(|s| s.public_policy_id) } - pub fn get_predefined_deno_function(name: String) -> Result { - if let Some(mat) = with_store(|s| s.predefined_deno_functions.get(&name).cloned()) { - Ok(mat) - } else if !PREDEFINED_DENO_FUNCTIONS.iter().any(|n| n == &name) { - Err(errors::unknown_predefined_function(&name, "deno")) + pub fn get_predefined_deno_function( + name: String, + param: Option, + ) -> Result { + let mat = PredefinedFunctionMatData::from_raw(name, param)?; + if let Some(mat_id) = with_store(|s| s.predefined_deno_functions.get(&mat).cloned()) { + Ok(mat_id) } else { let runtime_id = Store::get_deno_runtime(); - let mat = Store::register_materializer(Materializer { + let mat_id = Store::register_materializer(Materializer { runtime_id, effect: Effect::Read, - data: Rc::new(DenoMaterializer::Predefined(MaterializerDenoPredefined { - name: name.clone(), - })) - .into(), + data: Rc::new(DenoMaterializer::Predefined(mat.clone())).into(), }); with_store_mut(|s| { - s.predefined_deno_functions.insert(name, mat); + s.predefined_deno_functions.insert(mat, mat_id); }); - Ok(mat) + Ok(mat_id) } } diff --git a/src/typegraph/core/src/lib.rs b/src/typegraph/core/src/lib.rs index 8c33565b5..a3816d313 100644 --- a/src/typegraph/core/src/lib.rs +++ b/src/typegraph/core/src/lib.rs @@ -19,10 +19,10 @@ mod test_utils; use std::collections::HashSet; +use common::typegraph::runtimes::deno::{ContextCheckX, PredefinedFunctionMatData}; use common::typegraph::Injection; use errors::{Result, TgError}; use global_store::Store; -use indoc::formatdoc; use params::apply; use regex::Regex; use runtimes::{DenoMaterializer, Materializer}; @@ -38,7 +38,7 @@ use wit::core::{ TypeEither, TypeFile, TypeFloat, TypeFunc, TypeId as CoreTypeId, TypeInteger, TypeList, TypeOptional, TypeString, TypeStruct, TypeUnion, TypegraphInitParams, }; -use wit::runtimes::{Guest, MaterializerDenoFunc}; +use wit::runtimes::{Guest as _, MaterializerDenoPredefined}; pub mod wit { wit_bindgen::generate!({ @@ -52,6 +52,17 @@ pub mod wit { pub struct Lib {} +impl From for ContextCheckX { + fn from(check: ContextCheck) -> Self { + use ContextCheck as CC; + match check { + CC::NotNull => ContextCheckX::NotNull, + CC::Value(v) => ContextCheckX::Value(v), + CC::Pattern(p) => ContextCheckX::Pattern(p), + } + } +} + impl wit::core::Guest for Lib { fn init_typegraph(params: TypegraphInitParams) -> Result<()> { typegraph::init(params) @@ -185,9 +196,7 @@ impl wit::core::Guest for Lib { } fn get_internal_policy() -> Result<(PolicyId, String)> { - let deno_mat = DenoMaterializer::Predefined(wit::runtimes::MaterializerDenoPredefined { - name: "internal_policy".to_string(), - }); + let deno_mat = DenoMaterializer::Predefined(PredefinedFunctionMatData::InternalPolicy); let mat = Materializer::deno(deno_mat, crate::wit::runtimes::Effect::Read); let policy_id = Store::register_policy( Policy { @@ -213,42 +222,19 @@ impl wit::core::Guest for Lib { .replace_all(&name, "_") .to_string(); - let check = match check { - ContextCheck::NotNull => "value != null ? 'PASS' : 'DENY'".to_string(), - ContextCheck::Value(val) => { - format!( - "value === {} ? 'PASS' : 'DENY'", - serde_json::to_string(&val).unwrap() - ) - } - ContextCheck::Pattern(pattern) => { - format!( - "new RegExp({}).test(value) ? 'PASS' : 'DENY' ", - serde_json::to_string(&pattern).unwrap() - ) - } - }; + let check: ContextCheckX = check.into(); + let check = serde_json::json!({ + "key": key, + "value": check + }); - let key = serde_json::to_string(&key).unwrap(); - - let code = formatdoc! {r#" - (_, {{ context }}) => {{ - const chunks = {key}.split("."); - let value = context; - for (const chunk of chunks) {{ - value = value?.[chunk]; - }} - return {check}; - }} - "# }; - - let mat_id = Lib::register_deno_func( - MaterializerDenoFunc { - code, - secrets: vec![], - }, - wit::runtimes::Effect::Read, - )?; + let mat_id = Lib::get_predefined_deno_func(MaterializerDenoPredefined { + name: "context_check".to_string(), + param: Some( + serde_json::to_string(&check) + .map_err(|e| format!("Error while serializing context check: {e:?}"))?, + ), + })?; Lib::register_policy(Policy { name: name.clone(), diff --git a/src/typegraph/core/src/runtimes/deno.rs b/src/typegraph/core/src/runtimes/deno.rs index fe34beae5..eb89f6a0f 100644 --- a/src/typegraph/core/src/runtimes/deno.rs +++ b/src/typegraph/core/src/runtimes/deno.rs @@ -1,6 +1,8 @@ // Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. // SPDX-License-Identifier: MPL-2.0 +use common::typegraph::runtimes::deno::PredefinedFunctionMatData; + use crate::wit::runtimes as wit; #[derive(Debug)] @@ -25,7 +27,7 @@ pub struct MaterializerDenoStatic { pub enum DenoMaterializer { Static(MaterializerDenoStatic), Inline(wit::MaterializerDenoFunc), - Predefined(wit::MaterializerDenoPredefined), + Predefined(PredefinedFunctionMatData), Module(MaterializerDenoModule), Import(MaterializerDenoImport), } diff --git a/src/typegraph/core/src/runtimes/mod.rs b/src/typegraph/core/src/runtimes/mod.rs index 704b86296..3a555841e 100644 --- a/src/typegraph/core/src/runtimes/mod.rs +++ b/src/typegraph/core/src/runtimes/mod.rs @@ -295,7 +295,7 @@ impl crate::wit::runtimes::Guest for crate::Lib { fn get_predefined_deno_func( data: wit::MaterializerDenoPredefined, ) -> Result { - Store::get_predefined_deno_function(data.name) + Store::get_predefined_deno_function(data.name, data.param) } fn import_deno_function( @@ -654,6 +654,7 @@ impl crate::wit::runtimes::Guest for crate::Lib { WitOp::RawPrismaUpdate => (WitEffect::Update(false), Op::RawPrismaQuery), WitOp::RawPrismaDelete => (WitEffect::Delete(true), Op::RawPrismaQuery), WitOp::QueryPrismaModel => (WitEffect::Read, Op::QueryPrismaModel), + WitOp::Ping => (WitEffect::Read, Op::Ping), }; Ok(Store::register_materializer(Materializer::typegate( diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/input_type.rs b/src/typegraph/core/src/runtimes/prisma/type_generation/input_type.rs index c789035a7..62d9c5a79 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/input_type.rs +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/input_type.rs @@ -20,7 +20,7 @@ enum Operation { pub struct InputType { model_id: TypeId, - skip_rel: Vec, + skip_rel: std::collections::BTreeSet, operation: Operation, } @@ -28,7 +28,7 @@ impl InputType { pub fn for_create(model_id: TypeId) -> Self { Self { model_id, - skip_rel: vec![], + skip_rel: Default::default(), operation: Operation::Create, } } @@ -36,7 +36,7 @@ impl InputType { pub fn for_update(model_id: TypeId) -> Self { Self { model_id, - skip_rel: vec![], + skip_rel: Default::default(), operation: Operation::Update, } } @@ -66,7 +66,7 @@ impl TypeGen for InputType { model_id: prop.model_type.type_id, skip_rel: { let mut skip_rel = self.skip_rel.clone(); - skip_rel.push(rel_name.to_string()); + skip_rel.insert(rel_name.to_string()); skip_rel }, operation: Operation::Create, @@ -98,7 +98,7 @@ impl TypeGen for InputType { model_id: prop.model_type.type_id, skip_rel: { let mut skip_rel = self.skip_rel.clone(); - skip_rel.push(rel_name.to_string()); + skip_rel.insert(rel_name.to_string()); skip_rel }, operation: Operation::Update, @@ -218,7 +218,14 @@ impl TypeGen for InputType { let suffix = if self.skip_rel.is_empty() { "".to_string() } else { - format!("_excluding_{}", self.skip_rel.join("_and_")) + format!( + "_excluding_{}", + self.skip_rel + .iter() + .map(|owned| &owned[..]) + .collect::>() + .join("_and_") + ) }; let op = match self.operation { Operation::Create => "create", diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/out_type.rs b/src/typegraph/core/src/runtimes/prisma/type_generation/out_type.rs index 49667da73..993738bcb 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/out_type.rs +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/out_type.rs @@ -12,14 +12,14 @@ use super::{Cardinality, TypeGen}; pub struct OutType { model_id: TypeId, - skip_rel: Vec, // list of relationships to skip to avoid infinite recursion + skip_rel: std::collections::BTreeSet, // list of relationships to skip to avoid infinite recursion } impl OutType { pub fn new(model_id: TypeId) -> Self { Self { model_id, - skip_rel: vec![], + skip_rel: Default::default(), } } } @@ -45,7 +45,7 @@ impl TypeGen for OutType { } let mut skip_rel = self.skip_rel.clone(); - skip_rel.push(rel_name.clone()); + skip_rel.insert(rel_name.clone()); let out_type = context.generate(&OutType { model_id: prop.model_type.type_id, @@ -78,7 +78,14 @@ impl TypeGen for OutType { let suffix = if self.skip_rel.is_empty() { String::new() } else { - format!("_excluding_{}", self.skip_rel.join("_and_")) + format!( + "_excluding_{}", + self.skip_rel + .iter() + .map(|owned| &owned[..]) + .collect::>() + .join("_and_") + ) }; Ok(format!("{model_name}_output{suffix}")) } diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__query_where_expr__tests__Post__QueryWhereExpr.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__query_where_expr__tests__Post__QueryWhereExpr.snap index 25d5459f2..45a261aca 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__query_where_expr__tests__Post__QueryWhereExpr.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__query_where_expr__tests__Post__QueryWhereExpr.snap @@ -2,51 +2,33 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/query_where_expr.rs expression: "tree::print(post_where_expr)" --- -root: struct 'Post_query_where_input' #122 -├── [AND]: optional #119 -│ └── item: list #118 -│ └── item: &Post_query_where_input #117 -├── [NOT]: optional #120 -│ └── item: &Post_query_where_input #117 -├── [OR]: optional #119 -│ └── item: list #118 -│ └── item: &Post_query_where_input #117 -├── [author]: optional #114 -│ └── item: struct 'User_where_excluding_Post' #113 -│ ├── [id]: optional #95 +root: struct 'Post_query_where_input' #103 +├── [AND]: optional #100 +│ └── item: list #99 +│ └── item: &Post_query_where_input #98 +├── [NOT]: optional #101 +│ └── item: &Post_query_where_input #98 +├── [OR]: optional #100 +│ └── item: list #99 +│ └── item: &Post_query_where_input #98 +├── [author]: optional #95 +│ └── item: struct 'User_where_excluding_Post' #94 +│ ├── [id]: optional #82 │ │ └── item: &_prisma_integer_filter_ex #16 -│ ├── [name]: optional #96 +│ ├── [name]: optional #83 │ │ └── item: &_prisma_string_filter_ex #33 -│ └── [posts]: optional #111 -│ └── item: union #110 -│ ├── variant_0: struct #105 -│ │ └── [every]: optional #104 -│ │ └── item: struct 'Post_where_excluding_User_and_Post' #103 -│ │ ├── [author]: optional #101 -│ │ │ └── item: &User_where_excluding_Post #100 -│ │ ├── [id]: optional #98 -│ │ │ └── item: &_prisma_integer_filter_ex #16 -│ │ └── [title]: optional #99 -│ │ └── item: &_prisma_string_filter_ex #33 -│ ├── variant_1: struct #107 -│ │ └── [some]: optional #106 -│ │ └── item: struct 'Post_where_excluding_User_and_Post' #103 -│ │ ├── [author]: optional #101 -│ │ │ └── item: &User_where_excluding_Post #100 -│ │ ├── [id]: optional #98 -│ │ │ └── item: &_prisma_integer_filter_ex #16 -│ │ └── [title]: optional #99 -│ │ └── item: &_prisma_string_filter_ex #33 -│ └── variant_2: struct #109 -│ └── [none]: optional #108 -│ └── item: struct 'Post_where_excluding_User_and_Post' #103 -│ ├── [author]: optional #101 -│ │ └── item: &User_where_excluding_Post #100 -│ ├── [id]: optional #98 -│ │ └── item: &_prisma_integer_filter_ex #16 -│ └── [title]: optional #99 -│ └── item: &_prisma_string_filter_ex #33 -├── [id]: optional #92 +│ └── [posts]: optional #92 +│ └── item: union #91 +│ ├── variant_0: struct #86 +│ │ └── [every]: optional #85 +│ │ └── item: &Post_where #84 +│ ├── variant_1: struct #88 +│ │ └── [some]: optional #87 +│ │ └── item: &Post_where #84 +│ └── variant_2: struct #90 +│ └── [none]: optional #89 +│ └── item: &Post_where #84 +├── [id]: optional #79 │ └── item: &_prisma_integer_filter_ex #16 -└── [title]: optional #93 +└── [title]: optional #80 └── item: &_prisma_string_filter_ex #33 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__query_where_expr__tests__User__QueryWhereExpr.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__query_where_expr__tests__User__QueryWhereExpr.snap index 7fbcaffe7..c58832484 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__query_where_expr__tests__User__QueryWhereExpr.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__query_where_expr__tests__User__QueryWhereExpr.snap @@ -2,15 +2,15 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/query_where_expr.rs expression: "tree::print(user_where_expr)" --- -root: struct 'User_query_where_input' #89 -├── [AND]: optional #86 -│ └── item: list #85 -│ └── item: &User_query_where_input #84 -├── [NOT]: optional #87 -│ └── item: &User_query_where_input #84 -├── [OR]: optional #86 -│ └── item: list #85 -│ └── item: &User_query_where_input #84 +root: struct 'User_query_where_input' #76 +├── [AND]: optional #73 +│ └── item: list #72 +│ └── item: &User_query_where_input #71 +├── [NOT]: optional #74 +│ └── item: &User_query_where_input #71 +├── [OR]: optional #73 +│ └── item: list #72 +│ └── item: &User_query_where_input #71 ├── [id]: optional #32 │ └── item: optional '_prisma_integer_filter_ex' #31 │ └── item: union #29 @@ -107,76 +107,31 @@ root: struct 'User_query_where_input' #89 │ │ └── item: string #35 │ └── [startsWith]: optional #36 │ └── item: string #35 -└── [posts]: optional #81 - └── item: union #80 - ├── variant_0: struct #75 - │ └── [every]: optional #74 - │ └── item: struct 'Post_where_excluding_User' #73 - │ ├── [author]: optional #71 - │ │ └── item: struct 'User_where_excluding_Post_and_User' #70 - │ │ ├── [id]: optional #58 - │ │ │ └── item: &_prisma_integer_filter_ex #16 - │ │ ├── [name]: optional #59 - │ │ │ └── item: &_prisma_string_filter_ex #33 - │ │ └── [posts]: optional #68 - │ │ └── item: union #67 - │ │ ├── variant_0: struct #62 - │ │ │ └── [every]: optional #61 - │ │ │ └── item: &Post_where_excluding_User #60 - │ │ ├── variant_1: struct #64 - │ │ │ └── [some]: optional #63 - │ │ │ └── item: &Post_where_excluding_User #60 - │ │ └── variant_2: struct #66 - │ │ └── [none]: optional #65 - │ │ └── item: &Post_where_excluding_User #60 +└── [posts]: optional #68 + └── item: union #67 + ├── variant_0: struct #62 + │ └── [every]: optional #61 + │ └── item: struct 'Post_where_excluding_User' #60 + │ ├── [author]: optional #58 + │ │ └── item: &User_where #57 │ ├── [id]: optional #55 │ │ └── item: &_prisma_integer_filter_ex #16 │ └── [title]: optional #56 │ └── item: &_prisma_string_filter_ex #33 - ├── variant_1: struct #77 - │ └── [some]: optional #76 - │ └── item: struct 'Post_where_excluding_User' #73 - │ ├── [author]: optional #71 - │ │ └── item: struct 'User_where_excluding_Post_and_User' #70 - │ │ ├── [id]: optional #58 - │ │ │ └── item: &_prisma_integer_filter_ex #16 - │ │ ├── [name]: optional #59 - │ │ │ └── item: &_prisma_string_filter_ex #33 - │ │ └── [posts]: optional #68 - │ │ └── item: union #67 - │ │ ├── variant_0: struct #62 - │ │ │ └── [every]: optional #61 - │ │ │ └── item: &Post_where_excluding_User #60 - │ │ ├── variant_1: struct #64 - │ │ │ └── [some]: optional #63 - │ │ │ └── item: &Post_where_excluding_User #60 - │ │ └── variant_2: struct #66 - │ │ └── [none]: optional #65 - │ │ └── item: &Post_where_excluding_User #60 + ├── variant_1: struct #64 + │ └── [some]: optional #63 + │ └── item: struct 'Post_where_excluding_User' #60 + │ ├── [author]: optional #58 + │ │ └── item: &User_where #57 │ ├── [id]: optional #55 │ │ └── item: &_prisma_integer_filter_ex #16 │ └── [title]: optional #56 │ └── item: &_prisma_string_filter_ex #33 - └── variant_2: struct #79 - └── [none]: optional #78 - └── item: struct 'Post_where_excluding_User' #73 - ├── [author]: optional #71 - │ └── item: struct 'User_where_excluding_Post_and_User' #70 - │ ├── [id]: optional #58 - │ │ └── item: &_prisma_integer_filter_ex #16 - │ ├── [name]: optional #59 - │ │ └── item: &_prisma_string_filter_ex #33 - │ └── [posts]: optional #68 - │ └── item: union #67 - │ ├── variant_0: struct #62 - │ │ └── [every]: optional #61 - │ │ └── item: &Post_where_excluding_User #60 - │ ├── variant_1: struct #64 - │ │ └── [some]: optional #63 - │ │ └── item: &Post_where_excluding_User #60 - │ └── variant_2: struct #66 - │ └── [none]: optional #65 - │ └── item: &Post_where_excluding_User #60 + └── variant_2: struct #66 + └── [none]: optional #65 + └── item: struct 'Post_where_excluding_User' #60 + ├── [author]: optional #58 + │ └── item: &User_where #57 ├── [id]: optional #55 │ └── item: &_prisma_integer_filter_ex #16 └── [title]: optional #56 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_many Post inp.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_many Post inp.snap index 18b209d8e..b26f6cd23 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_many Post inp.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_many Post inp.snap @@ -2,76 +2,58 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/mod.rs expression: tp.print(inp) --- -root: struct 'Post_query_input' #262 -├─ [cursor]: optional #255 -│ └─ item: union 'Post_cursor' #254 -│ ├─ variant_0: struct #251 +root: struct 'Post_query_input' #243 +├─ [cursor]: optional #236 +│ └─ item: union 'Post_cursor' #235 +│ ├─ variant_0: struct #232 │ │ └─ [id]: integer #119 -│ └─ variant_1: struct #252 +│ └─ variant_1: struct #233 │ └─ [title]: string #120 -├─ [distinct]: optional #260 -│ └─ item: list 'Post_keys_union' #259 -│ └─ item: string #257 enum{ '"id"', '"title"', '"author"' } -├─ [orderBy]: optional #247 -│ └─ item: list 'Post_order_by' #246 -│ └─ item: struct #244 -│ ├─ [author]: optional #243 -│ │ └─ item: list 'User_order_by_withoutrel_Post_User' #242 -│ │ └─ item: struct #240 +├─ [distinct]: optional #241 +│ └─ item: list 'Post_keys_union' #240 +│ └─ item: string #238 enum{ '"id"', '"title"', '"author"' } +├─ [orderBy]: optional #228 +│ └─ item: list 'Post_order_by' #227 +│ └─ item: struct #225 +│ ├─ [author]: optional #224 +│ │ └─ item: list 'User_order_by_withoutrel_Post_User' #223 +│ │ └─ item: struct #221 │ │ ├─ [id]: &_prisma_sort #63 │ │ └─ [name]: &_prisma_sort #63 │ ├─ [id]: &_prisma_sort #63 │ └─ [title]: &_prisma_sort #63 -├─ [skip]: optional #249 +├─ [skip]: optional #230 │ └─ item: &_skip #87 -├─ [take]: optional #248 +├─ [take]: optional #229 │ └─ item: &_take #83 -└─ [where]: optional #237 - └─ item: struct 'Post_query_where_input' #236 - ├─ [AND]: optional #233 - │ └─ item: list #232 - │ └─ item: &Post_query_where_input #231 - ├─ [NOT]: optional #234 - │ └─ item: &Post_query_where_input #231 - ├─ [OR]: optional #233 - │ └─ item: list #232 - │ └─ item: &Post_query_where_input #231 - ├─ [author]: optional #228 - │ └─ item: struct 'User_where_excluding_Post' #227 - │ ├─ [id]: optional #209 +└─ [where]: optional #218 + └─ item: struct 'Post_query_where_input' #217 + ├─ [AND]: optional #214 + │ └─ item: list #213 + │ └─ item: &Post_query_where_input #212 + ├─ [NOT]: optional #215 + │ └─ item: &Post_query_where_input #212 + ├─ [OR]: optional #214 + │ └─ item: list #213 + │ └─ item: &Post_query_where_input #212 + ├─ [author]: optional #209 + │ └─ item: struct 'User_where_excluding_Post' #208 + │ ├─ [id]: optional #196 │ │ └─ item: &_prisma_integer_filter_ex #35 - │ ├─ [name]: optional #210 + │ ├─ [name]: optional #197 │ │ └─ item: &_prisma_string_filter_ex #13 - │ └─ [posts]: optional #225 - │ └─ item: union #224 - │ ├─ variant_0: struct #219 - │ │ └─ [every]: optional #218 - │ │ └─ item: struct 'Post_where_excluding_User_and_Post' #217 - │ │ ├─ [author]: optional #215 - │ │ │ └─ item: &User_where_excluding_Post #214 - │ │ ├─ [id]: optional #212 - │ │ │ └─ item: &_prisma_integer_filter_ex #35 - │ │ └─ [title]: optional #213 - │ │ └─ item: &_prisma_string_filter_ex #13 - │ ├─ variant_1: struct #221 - │ │ └─ [some]: optional #220 - │ │ └─ item: struct 'Post_where_excluding_User_and_Post' #217 - │ │ ├─ [author]: optional #215 - │ │ │ └─ item: &User_where_excluding_Post #214 - │ │ ├─ [id]: optional #212 - │ │ │ └─ item: &_prisma_integer_filter_ex #35 - │ │ └─ [title]: optional #213 - │ │ └─ item: &_prisma_string_filter_ex #13 - │ └─ variant_2: struct #223 - │ └─ [none]: optional #222 - │ └─ item: struct 'Post_where_excluding_User_and_Post' #217 - │ ├─ [author]: optional #215 - │ │ └─ item: &User_where_excluding_Post #214 - │ ├─ [id]: optional #212 - │ │ └─ item: &_prisma_integer_filter_ex #35 - │ └─ [title]: optional #213 - │ └─ item: &_prisma_string_filter_ex #13 - ├─ [id]: optional #206 + │ └─ [posts]: optional #206 + │ └─ item: union #205 + │ ├─ variant_0: struct #200 + │ │ └─ [every]: optional #199 + │ │ └─ item: &Post_where #198 + │ ├─ variant_1: struct #202 + │ │ └─ [some]: optional #201 + │ │ └─ item: &Post_where #198 + │ └─ variant_2: struct #204 + │ └─ [none]: optional #203 + │ └─ item: &Post_where #198 + ├─ [id]: optional #193 │ └─ item: &_prisma_integer_filter_ex #35 - └─ [title]: optional #207 + └─ [title]: optional #194 └─ item: &_prisma_string_filter_ex #13 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_many Post out.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_many Post out.snap index b9bef9ed9..6ee2f4695 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_many Post out.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_many Post out.snap @@ -2,8 +2,8 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/mod.rs expression: tp.print(out) --- -root: list #266 -└─ item: struct 'Post_with_nested_count' #265 +root: list #247 +└─ item: struct 'Post_with_nested_count' #246 ├─ [author]: &User #121 ├─ [id]: integer #117 └─ [title]: string #120 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_many User inp.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_many User inp.snap index 48218417a..209be20d3 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_many User inp.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_many User inp.snap @@ -2,116 +2,71 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/mod.rs expression: tp.print(inp) --- -root: struct 'User_query_input' #189 -├─ [cursor]: optional #182 -│ └─ item: union 'User_cursor' #181 -│ ├─ variant_0: struct #178 +root: struct 'User_query_input' #176 +├─ [cursor]: optional #169 +│ └─ item: union 'User_cursor' #168 +│ ├─ variant_0: struct #165 │ │ └─ [id]: integer #111 -│ └─ variant_1: struct #179 +│ └─ variant_1: struct #166 │ └─ [name]: string #112 -├─ [distinct]: optional #187 -│ └─ item: list 'User_keys_union' #186 -│ └─ item: string #184 enum{ '"id"', '"name"', '"posts"' } -├─ [orderBy]: optional #174 -│ └─ item: list 'User_order_by' #173 -│ └─ item: struct #171 +├─ [distinct]: optional #174 +│ └─ item: list 'User_keys_union' #173 +│ └─ item: string #171 enum{ '"id"', '"name"', '"posts"' } +├─ [orderBy]: optional #161 +│ └─ item: list 'User_order_by' #160 +│ └─ item: struct #158 │ ├─ [id]: &_prisma_sort #63 │ ├─ [name]: &_prisma_sort #63 -│ └─ [posts]: optional '_prisma_sort_by_aggregates' #170 -│ └─ item: struct #168 +│ └─ [posts]: optional '_prisma_sort_by_aggregates' #157 +│ └─ item: struct #155 │ ├─ [_avg]: &_prisma_sort #63 │ ├─ [_count]: &_prisma_sort #63 │ ├─ [_max]: &_prisma_sort #63 │ ├─ [_min]: &_prisma_sort #63 │ └─ [_sum]: &_prisma_sort #63 -├─ [skip]: optional #176 +├─ [skip]: optional #163 │ └─ item: &_skip #87 -├─ [take]: optional #175 +├─ [take]: optional #162 │ └─ item: &_take #83 -└─ [where]: optional #165 - └─ item: struct 'User_query_where_input' #164 - ├─ [AND]: optional #161 - │ └─ item: list #160 - │ └─ item: &User_query_where_input #159 - ├─ [NOT]: optional #162 - │ └─ item: &User_query_where_input #159 - ├─ [OR]: optional #161 - │ └─ item: list #160 - │ └─ item: &User_query_where_input #159 +└─ [where]: optional #152 + └─ item: struct 'User_query_where_input' #151 + ├─ [AND]: optional #148 + │ └─ item: list #147 + │ └─ item: &User_query_where_input #146 + ├─ [NOT]: optional #149 + │ └─ item: &User_query_where_input #146 + ├─ [OR]: optional #148 + │ └─ item: list #147 + │ └─ item: &User_query_where_input #146 ├─ [id]: optional #127 │ └─ item: &_prisma_integer_filter_ex #35 ├─ [name]: optional #128 │ └─ item: &_prisma_string_filter_ex #13 - └─ [posts]: optional #156 - └─ item: union #155 - ├─ variant_0: struct #150 - │ └─ [every]: optional #149 - │ └─ item: struct 'Post_where_excluding_User' #148 - │ ├─ [author]: optional #146 - │ │ └─ item: struct 'User_where_excluding_Post_and_User' #145 - │ │ ├─ [id]: optional #133 - │ │ │ └─ item: &_prisma_integer_filter_ex #35 - │ │ ├─ [name]: optional #134 - │ │ │ └─ item: &_prisma_string_filter_ex #13 - │ │ └─ [posts]: optional #143 - │ │ └─ item: union #142 - │ │ ├─ variant_0: struct #137 - │ │ │ └─ [every]: optional #136 - │ │ │ └─ item: &Post_where_excluding_User #135 - │ │ ├─ variant_1: struct #139 - │ │ │ └─ [some]: optional #138 - │ │ │ └─ item: &Post_where_excluding_User #135 - │ │ └─ variant_2: struct #141 - │ │ └─ [none]: optional #140 - │ │ └─ item: &Post_where_excluding_User #135 + └─ [posts]: optional #143 + └─ item: union #142 + ├─ variant_0: struct #137 + │ └─ [every]: optional #136 + │ └─ item: struct 'Post_where_excluding_User' #135 + │ ├─ [author]: optional #133 + │ │ └─ item: &User_where #132 │ ├─ [id]: optional #130 │ │ └─ item: &_prisma_integer_filter_ex #35 │ └─ [title]: optional #131 │ └─ item: &_prisma_string_filter_ex #13 - ├─ variant_1: struct #152 - │ └─ [some]: optional #151 - │ └─ item: struct 'Post_where_excluding_User' #148 - │ ├─ [author]: optional #146 - │ │ └─ item: struct 'User_where_excluding_Post_and_User' #145 - │ │ ├─ [id]: optional #133 - │ │ │ └─ item: &_prisma_integer_filter_ex #35 - │ │ ├─ [name]: optional #134 - │ │ │ └─ item: &_prisma_string_filter_ex #13 - │ │ └─ [posts]: optional #143 - │ │ └─ item: union #142 - │ │ ├─ variant_0: struct #137 - │ │ │ └─ [every]: optional #136 - │ │ │ └─ item: &Post_where_excluding_User #135 - │ │ ├─ variant_1: struct #139 - │ │ │ └─ [some]: optional #138 - │ │ │ └─ item: &Post_where_excluding_User #135 - │ │ └─ variant_2: struct #141 - │ │ └─ [none]: optional #140 - │ │ └─ item: &Post_where_excluding_User #135 + ├─ variant_1: struct #139 + │ └─ [some]: optional #138 + │ └─ item: struct 'Post_where_excluding_User' #135 + │ ├─ [author]: optional #133 + │ │ └─ item: &User_where #132 │ ├─ [id]: optional #130 │ │ └─ item: &_prisma_integer_filter_ex #35 │ └─ [title]: optional #131 │ └─ item: &_prisma_string_filter_ex #13 - └─ variant_2: struct #154 - └─ [none]: optional #153 - └─ item: struct 'Post_where_excluding_User' #148 - ├─ [author]: optional #146 - │ └─ item: struct 'User_where_excluding_Post_and_User' #145 - │ ├─ [id]: optional #133 - │ │ └─ item: &_prisma_integer_filter_ex #35 - │ ├─ [name]: optional #134 - │ │ └─ item: &_prisma_string_filter_ex #13 - │ └─ [posts]: optional #143 - │ └─ item: union #142 - │ ├─ variant_0: struct #137 - │ │ └─ [every]: optional #136 - │ │ └─ item: &Post_where_excluding_User #135 - │ ├─ variant_1: struct #139 - │ │ └─ [some]: optional #138 - │ │ └─ item: &Post_where_excluding_User #135 - │ └─ variant_2: struct #141 - │ └─ [none]: optional #140 - │ └─ item: &Post_where_excluding_User #135 + └─ variant_2: struct #141 + └─ [none]: optional #140 + └─ item: struct 'Post_where_excluding_User' #135 + ├─ [author]: optional #133 + │ └─ item: &User_where #132 ├─ [id]: optional #130 │ └─ item: &_prisma_integer_filter_ex #35 └─ [title]: optional #131 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_many User out.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_many User out.snap index 5b84668a2..d4c6062f9 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_many User out.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_many User out.snap @@ -2,14 +2,14 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/mod.rs expression: tp.print(out) --- -root: list #202 -└─ item: struct 'User_with_nested_count' #201 - ├─ [_count]: struct #199 - │ └─ [posts]: optional '_count' #198 - │ └─ item: integer #196 +root: list #189 +└─ item: struct 'User_with_nested_count' #188 + ├─ [_count]: struct #186 + │ └─ [posts]: optional '_count' #185 + │ └─ item: integer #183 ├─ [id]: integer #110 ├─ [name]: string #112 - └─ [posts]: list #194 - └─ item: struct 'Post_with_nested_count_excluding_rel_Post_User' #193 + └─ [posts]: list #181 + └─ item: struct 'Post_with_nested_count_excluding_rel_Post_User' #180 ├─ [id]: integer #117 └─ [title]: string #120 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_unique Post inp.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_unique Post inp.snap index dc008b06c..06cac004d 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_unique Post inp.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_unique Post inp.snap @@ -2,53 +2,35 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/mod.rs expression: tp.print(inp) --- -root: struct #169 -└─ [where]: optional #168 - └─ item: struct 'Post_query_where_unique_input' #167 - ├─ [AND]: optional #164 - │ └─ item: list #163 - │ └─ item: &Post_query_where_unique_input #162 - ├─ [NOT]: optional #165 - │ └─ item: &Post_query_where_unique_input #162 - ├─ [OR]: optional #164 - │ └─ item: list #163 - │ └─ item: &Post_query_where_unique_input #162 - ├─ [author]: optional #159 - │ └─ item: struct 'User_where_excluding_Post' #158 - │ ├─ [id]: optional #140 +root: struct #150 +└─ [where]: optional #149 + └─ item: struct 'Post_query_where_unique_input' #148 + ├─ [AND]: optional #145 + │ └─ item: list #144 + │ └─ item: &Post_query_where_unique_input #143 + ├─ [NOT]: optional #146 + │ └─ item: &Post_query_where_unique_input #143 + ├─ [OR]: optional #145 + │ └─ item: list #144 + │ └─ item: &Post_query_where_unique_input #143 + ├─ [author]: optional #140 + │ └─ item: struct 'User_where_excluding_Post' #139 + │ ├─ [id]: optional #127 │ │ └─ item: &_prisma_integer_filter_ex #34 - │ ├─ [name]: optional #141 + │ ├─ [name]: optional #128 │ │ └─ item: &_prisma_string_filter_ex #12 - │ └─ [posts]: optional #156 - │ └─ item: union #155 - │ ├─ variant_0: struct #150 - │ │ └─ [every]: optional #149 - │ │ └─ item: struct 'Post_where_excluding_User_and_Post' #148 - │ │ ├─ [author]: optional #146 - │ │ │ └─ item: &User_where_excluding_Post #145 - │ │ ├─ [id]: optional #143 - │ │ │ └─ item: &_prisma_integer_filter_ex #34 - │ │ └─ [title]: optional #144 - │ │ └─ item: &_prisma_string_filter_ex #12 - │ ├─ variant_1: struct #152 - │ │ └─ [some]: optional #151 - │ │ └─ item: struct 'Post_where_excluding_User_and_Post' #148 - │ │ ├─ [author]: optional #146 - │ │ │ └─ item: &User_where_excluding_Post #145 - │ │ ├─ [id]: optional #143 - │ │ │ └─ item: &_prisma_integer_filter_ex #34 - │ │ └─ [title]: optional #144 - │ │ └─ item: &_prisma_string_filter_ex #12 - │ └─ variant_2: struct #154 - │ └─ [none]: optional #153 - │ └─ item: struct 'Post_where_excluding_User_and_Post' #148 - │ ├─ [author]: optional #146 - │ │ └─ item: &User_where_excluding_Post #145 - │ ├─ [id]: optional #143 - │ │ └─ item: &_prisma_integer_filter_ex #34 - │ └─ [title]: optional #144 - │ └─ item: &_prisma_string_filter_ex #12 - ├─ [id]: optional #137 + │ └─ [posts]: optional #137 + │ └─ item: union #136 + │ ├─ variant_0: struct #131 + │ │ └─ [every]: optional #130 + │ │ └─ item: &Post_where #129 + │ ├─ variant_1: struct #133 + │ │ └─ [some]: optional #132 + │ │ └─ item: &Post_where #129 + │ └─ variant_2: struct #135 + │ └─ [none]: optional #134 + │ └─ item: &Post_where #129 + ├─ [id]: optional #124 │ └─ item: &_prisma_integer_filter_ex #34 - └─ [title]: optional #138 + └─ [title]: optional #125 └─ item: &_prisma_string_filter_ex #12 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_unique Post out.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_unique Post out.snap index 23dad0dfb..97b80d078 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_unique Post out.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_unique Post out.snap @@ -2,8 +2,8 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/mod.rs expression: tp.print(out) --- -root: optional #173 -└─ item: struct 'Post_with_nested_count' #172 +root: optional #154 +└─ item: struct 'Post_with_nested_count' #153 ├─ [author]: &User #77 ├─ [id]: integer #73 └─ [title]: string #76 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_unique User inp.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_unique User inp.snap index 97c231ae4..cd4a5c8b0 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_unique User inp.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_unique User inp.snap @@ -2,91 +2,46 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/mod.rs expression: tp.print(inp) --- -root: struct #121 -└─ [where]: optional #120 - └─ item: struct 'User_query_where_unique_input' #119 - ├─ [AND]: optional #116 - │ └─ item: list #115 - │ └─ item: &User_query_where_unique_input #114 - ├─ [NOT]: optional #117 - │ └─ item: &User_query_where_unique_input #114 - ├─ [OR]: optional #116 - │ └─ item: list #115 - │ └─ item: &User_query_where_unique_input #114 +root: struct #108 +└─ [where]: optional #107 + └─ item: struct 'User_query_where_unique_input' #106 + ├─ [AND]: optional #103 + │ └─ item: list #102 + │ └─ item: &User_query_where_unique_input #101 + ├─ [NOT]: optional #104 + │ └─ item: &User_query_where_unique_input #101 + ├─ [OR]: optional #103 + │ └─ item: list #102 + │ └─ item: &User_query_where_unique_input #101 ├─ [id]: optional #82 │ └─ item: &_prisma_integer_filter_ex #34 ├─ [name]: optional #83 │ └─ item: &_prisma_string_filter_ex #12 - └─ [posts]: optional #111 - └─ item: union #110 - ├─ variant_0: struct #105 - │ └─ [every]: optional #104 - │ └─ item: struct 'Post_where_excluding_User' #103 - │ ├─ [author]: optional #101 - │ │ └─ item: struct 'User_where_excluding_Post_and_User' #100 - │ │ ├─ [id]: optional #88 - │ │ │ └─ item: &_prisma_integer_filter_ex #34 - │ │ ├─ [name]: optional #89 - │ │ │ └─ item: &_prisma_string_filter_ex #12 - │ │ └─ [posts]: optional #98 - │ │ └─ item: union #97 - │ │ ├─ variant_0: struct #92 - │ │ │ └─ [every]: optional #91 - │ │ │ └─ item: &Post_where_excluding_User #90 - │ │ ├─ variant_1: struct #94 - │ │ │ └─ [some]: optional #93 - │ │ │ └─ item: &Post_where_excluding_User #90 - │ │ └─ variant_2: struct #96 - │ │ └─ [none]: optional #95 - │ │ └─ item: &Post_where_excluding_User #90 + └─ [posts]: optional #98 + └─ item: union #97 + ├─ variant_0: struct #92 + │ └─ [every]: optional #91 + │ └─ item: struct 'Post_where_excluding_User' #90 + │ ├─ [author]: optional #88 + │ │ └─ item: &User_where #87 │ ├─ [id]: optional #85 │ │ └─ item: &_prisma_integer_filter_ex #34 │ └─ [title]: optional #86 │ └─ item: &_prisma_string_filter_ex #12 - ├─ variant_1: struct #107 - │ └─ [some]: optional #106 - │ └─ item: struct 'Post_where_excluding_User' #103 - │ ├─ [author]: optional #101 - │ │ └─ item: struct 'User_where_excluding_Post_and_User' #100 - │ │ ├─ [id]: optional #88 - │ │ │ └─ item: &_prisma_integer_filter_ex #34 - │ │ ├─ [name]: optional #89 - │ │ │ └─ item: &_prisma_string_filter_ex #12 - │ │ └─ [posts]: optional #98 - │ │ └─ item: union #97 - │ │ ├─ variant_0: struct #92 - │ │ │ └─ [every]: optional #91 - │ │ │ └─ item: &Post_where_excluding_User #90 - │ │ ├─ variant_1: struct #94 - │ │ │ └─ [some]: optional #93 - │ │ │ └─ item: &Post_where_excluding_User #90 - │ │ └─ variant_2: struct #96 - │ │ └─ [none]: optional #95 - │ │ └─ item: &Post_where_excluding_User #90 + ├─ variant_1: struct #94 + │ └─ [some]: optional #93 + │ └─ item: struct 'Post_where_excluding_User' #90 + │ ├─ [author]: optional #88 + │ │ └─ item: &User_where #87 │ ├─ [id]: optional #85 │ │ └─ item: &_prisma_integer_filter_ex #34 │ └─ [title]: optional #86 │ └─ item: &_prisma_string_filter_ex #12 - └─ variant_2: struct #109 - └─ [none]: optional #108 - └─ item: struct 'Post_where_excluding_User' #103 - ├─ [author]: optional #101 - │ └─ item: struct 'User_where_excluding_Post_and_User' #100 - │ ├─ [id]: optional #88 - │ │ └─ item: &_prisma_integer_filter_ex #34 - │ ├─ [name]: optional #89 - │ │ └─ item: &_prisma_string_filter_ex #12 - │ └─ [posts]: optional #98 - │ └─ item: union #97 - │ ├─ variant_0: struct #92 - │ │ └─ [every]: optional #91 - │ │ └─ item: &Post_where_excluding_User #90 - │ ├─ variant_1: struct #94 - │ │ └─ [some]: optional #93 - │ │ └─ item: &Post_where_excluding_User #90 - │ └─ variant_2: struct #96 - │ └─ [none]: optional #95 - │ └─ item: &Post_where_excluding_User #90 + └─ variant_2: struct #96 + └─ [none]: optional #95 + └─ item: struct 'Post_where_excluding_User' #90 + ├─ [author]: optional #88 + │ └─ item: &User_where #87 ├─ [id]: optional #85 │ └─ item: &_prisma_integer_filter_ex #34 └─ [title]: optional #86 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_unique User out.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_unique User out.snap index 10894dc1c..adbfac68d 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_unique User out.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__find_unique User out.snap @@ -2,14 +2,14 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/mod.rs expression: tp.print(out) --- -root: optional #134 -└─ item: struct 'User_with_nested_count' #133 - ├─ [_count]: struct #131 - │ └─ [posts]: optional '_count' #130 - │ └─ item: integer #128 +root: optional #121 +└─ item: struct 'User_with_nested_count' #120 + ├─ [_count]: struct #118 + │ └─ [posts]: optional '_count' #117 + │ └─ item: integer #115 ├─ [id]: integer #66 ├─ [name]: string #68 - └─ [posts]: list #126 - └─ item: struct 'Post_with_nested_count_excluding_rel_Post_User' #125 + └─ [posts]: list #113 + └─ item: struct 'Post_with_nested_count_excluding_rel_Post_User' #112 ├─ [id]: integer #73 └─ [title]: string #76 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__group_by Post inp.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__group_by Post inp.snap index 4551206a7..f6beec571 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__group_by Post inp.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__group_by Post inp.snap @@ -2,142 +2,106 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/mod.rs expression: tp.print(inp) --- -root: struct 'Post_group_by_query_input' #425 -├─ [by]: list 'Post_group_by' #387 -│ └─ item: string #385 enum{ '"id"', '"title"' } -├─ [cursor]: optional #383 -│ └─ item: union 'Post_cursor' #382 -│ ├─ variant_0: struct #379 +root: struct 'Post_group_by_query_input' #387 +├─ [by]: list 'Post_group_by' #355 +│ └─ item: string #353 enum{ '"id"', '"title"' } +├─ [cursor]: optional #351 +│ └─ item: union 'Post_cursor' #350 +│ ├─ variant_0: struct #347 │ │ └─ [id]: integer #183 -│ └─ variant_1: struct #380 +│ └─ variant_1: struct #348 │ └─ [title]: string #184 -├─ [having]: optional #423 -│ └─ item: union 'Post_having' #422 -│ ├─ variant_0: struct 'Post_where_with_aggregates' #414 -│ │ ├─ [author]: optional #412 -│ │ │ └─ item: struct 'User_where_with_aggregates_excluding_Post' #411 -│ │ │ ├─ [id]: optional #393 +├─ [having]: optional #385 +│ └─ item: union 'Post_having' #384 +│ ├─ variant_0: struct 'Post_where_with_aggregates' #376 +│ │ ├─ [author]: optional #374 +│ │ │ └─ item: struct 'User_where_with_aggregates_excluding_Post' #373 +│ │ │ ├─ [id]: optional #361 │ │ │ │ └─ item: &_prisma_integer_filter_with_aggregates_ex #114 -│ │ │ ├─ [name]: optional #394 +│ │ │ ├─ [name]: optional #362 │ │ │ │ └─ item: &_prisma_string_filter_ex #13 -│ │ │ └─ [posts]: optional #409 -│ │ │ └─ item: union #408 -│ │ │ ├─ variant_0: struct #403 -│ │ │ │ └─ [every]: optional #402 -│ │ │ │ └─ item: struct 'Post_where_with_aggregates_excluding_User_and_Post' #401 -│ │ │ │ ├─ [author]: optional #399 -│ │ │ │ │ └─ item: &User_where_with_aggregates_excluding_Post #398 -│ │ │ │ ├─ [id]: optional #396 -│ │ │ │ │ └─ item: &_prisma_integer_filter_with_aggregates_ex #114 -│ │ │ │ └─ [title]: optional #397 -│ │ │ │ └─ item: &_prisma_string_filter_ex #13 -│ │ │ ├─ variant_1: struct #405 -│ │ │ │ └─ [some]: optional #404 -│ │ │ │ └─ item: struct 'Post_where_with_aggregates_excluding_User_and_Post' #401 -│ │ │ │ ├─ [author]: optional #399 -│ │ │ │ │ └─ item: &User_where_with_aggregates_excluding_Post #398 -│ │ │ │ ├─ [id]: optional #396 -│ │ │ │ │ └─ item: &_prisma_integer_filter_with_aggregates_ex #114 -│ │ │ │ └─ [title]: optional #397 -│ │ │ │ └─ item: &_prisma_string_filter_ex #13 -│ │ │ └─ variant_2: struct #407 -│ │ │ └─ [none]: optional #406 -│ │ │ └─ item: struct 'Post_where_with_aggregates_excluding_User_and_Post' #401 -│ │ │ ├─ [author]: optional #399 -│ │ │ │ └─ item: &User_where_with_aggregates_excluding_Post #398 -│ │ │ ├─ [id]: optional #396 -│ │ │ │ └─ item: &_prisma_integer_filter_with_aggregates_ex #114 -│ │ │ └─ [title]: optional #397 -│ │ │ └─ item: &_prisma_string_filter_ex #13 -│ │ ├─ [id]: optional #390 +│ │ │ └─ [posts]: optional #371 +│ │ │ └─ item: union #370 +│ │ │ ├─ variant_0: struct #365 +│ │ │ │ └─ [every]: optional #364 +│ │ │ │ └─ item: &Post_where_with_aggregates #363 +│ │ │ ├─ variant_1: struct #367 +│ │ │ │ └─ [some]: optional #366 +│ │ │ │ └─ item: &Post_where_with_aggregates #363 +│ │ │ └─ variant_2: struct #369 +│ │ │ └─ [none]: optional #368 +│ │ │ └─ item: &Post_where_with_aggregates #363 +│ │ ├─ [id]: optional #358 │ │ │ └─ item: &_prisma_integer_filter_with_aggregates_ex #114 -│ │ └─ [title]: optional #391 +│ │ └─ [title]: optional #359 │ │ └─ item: &_prisma_string_filter_ex #13 -│ ├─ variant_1: struct #417 -│ │ └─ [AND]: list #416 -│ │ └─ item: &Post_having #415 -│ ├─ variant_2: struct #419 -│ │ └─ [OR]: list #418 -│ │ └─ item: &Post_having #415 -│ └─ variant_3: struct #420 -│ └─ [NOT]: &Post_having #415 -├─ [orderBy]: optional #375 -│ └─ item: list 'Post_order_by_with_aggregates' #374 -│ └─ item: struct #372 -│ ├─ [_avg]: optional #364 -│ │ └─ item: struct #363 +│ ├─ variant_1: struct #379 +│ │ └─ [AND]: list #378 +│ │ └─ item: &Post_having #377 +│ ├─ variant_2: struct #381 +│ │ └─ [OR]: list #380 +│ │ └─ item: &Post_having #377 +│ └─ variant_3: struct #382 +│ └─ [NOT]: &Post_having #377 +├─ [orderBy]: optional #343 +│ └─ item: list 'Post_order_by_with_aggregates' #342 +│ └─ item: struct #340 +│ ├─ [_avg]: optional #332 +│ │ └─ item: struct #331 │ │ └─ [id]: &_prisma_sort #64 -│ ├─ [_count]: optional #362 -│ │ └─ item: struct #361 +│ ├─ [_count]: optional #330 +│ │ └─ item: struct #329 │ │ ├─ [author]: &_prisma_sort #64 │ │ ├─ [id]: &_prisma_sort #64 │ │ └─ [title]: &_prisma_sort #64 -│ ├─ [_max]: optional #364 -│ │ └─ item: struct #363 +│ ├─ [_max]: optional #332 +│ │ └─ item: struct #331 │ │ └─ [id]: &_prisma_sort #64 -│ ├─ [_min]: optional #364 -│ │ └─ item: struct #363 +│ ├─ [_min]: optional #332 +│ │ └─ item: struct #331 │ │ └─ [id]: &_prisma_sort #64 -│ ├─ [_sum]: optional #364 -│ │ └─ item: struct #363 +│ ├─ [_sum]: optional #332 +│ │ └─ item: struct #331 │ │ └─ [id]: &_prisma_sort #64 -│ ├─ [author]: optional #371 -│ │ └─ item: list 'User_order_by_withoutrel_Post_User' #370 -│ │ └─ item: struct #368 +│ ├─ [author]: optional #339 +│ │ └─ item: list 'User_order_by_withoutrel_Post_User' #338 +│ │ └─ item: struct #336 │ │ ├─ [id]: &_prisma_sort #64 │ │ └─ [name]: &_prisma_sort #64 │ ├─ [id]: &_prisma_sort #64 │ └─ [title]: &_prisma_sort #64 -├─ [skip]: optional #377 +├─ [skip]: optional #345 │ └─ item: &_skip #94 -├─ [take]: optional #376 +├─ [take]: optional #344 │ └─ item: &_take #90 -└─ [where]: optional #358 - └─ item: struct 'Post_query_where_input' #357 - ├─ [AND]: optional #354 - │ └─ item: list #353 - │ └─ item: &Post_query_where_input #352 - ├─ [NOT]: optional #355 - │ └─ item: &Post_query_where_input #352 - ├─ [OR]: optional #354 - │ └─ item: list #353 - │ └─ item: &Post_query_where_input #352 - ├─ [author]: optional #349 - │ └─ item: struct 'User_where_excluding_Post' #348 - │ ├─ [id]: optional #330 +└─ [where]: optional #326 + └─ item: struct 'Post_query_where_input' #325 + ├─ [AND]: optional #322 + │ └─ item: list #321 + │ └─ item: &Post_query_where_input #320 + ├─ [NOT]: optional #323 + │ └─ item: &Post_query_where_input #320 + ├─ [OR]: optional #322 + │ └─ item: list #321 + │ └─ item: &Post_query_where_input #320 + ├─ [author]: optional #317 + │ └─ item: struct 'User_where_excluding_Post' #316 + │ ├─ [id]: optional #304 │ │ └─ item: &_prisma_integer_filter_ex #35 - │ ├─ [name]: optional #331 + │ ├─ [name]: optional #305 │ │ └─ item: &_prisma_string_filter_ex #13 - │ └─ [posts]: optional #346 - │ └─ item: union #345 - │ ├─ variant_0: struct #340 - │ │ └─ [every]: optional #339 - │ │ └─ item: struct 'Post_where_excluding_User_and_Post' #338 - │ │ ├─ [author]: optional #336 - │ │ │ └─ item: &User_where_excluding_Post #335 - │ │ ├─ [id]: optional #333 - │ │ │ └─ item: &_prisma_integer_filter_ex #35 - │ │ └─ [title]: optional #334 - │ │ └─ item: &_prisma_string_filter_ex #13 - │ ├─ variant_1: struct #342 - │ │ └─ [some]: optional #341 - │ │ └─ item: struct 'Post_where_excluding_User_and_Post' #338 - │ │ ├─ [author]: optional #336 - │ │ │ └─ item: &User_where_excluding_Post #335 - │ │ ├─ [id]: optional #333 - │ │ │ └─ item: &_prisma_integer_filter_ex #35 - │ │ └─ [title]: optional #334 - │ │ └─ item: &_prisma_string_filter_ex #13 - │ └─ variant_2: struct #344 - │ └─ [none]: optional #343 - │ └─ item: struct 'Post_where_excluding_User_and_Post' #338 - │ ├─ [author]: optional #336 - │ │ └─ item: &User_where_excluding_Post #335 - │ ├─ [id]: optional #333 - │ │ └─ item: &_prisma_integer_filter_ex #35 - │ └─ [title]: optional #334 - │ └─ item: &_prisma_string_filter_ex #13 - ├─ [id]: optional #327 + │ └─ [posts]: optional #314 + │ └─ item: union #313 + │ ├─ variant_0: struct #308 + │ │ └─ [every]: optional #307 + │ │ └─ item: &Post_where #306 + │ ├─ variant_1: struct #310 + │ │ └─ [some]: optional #309 + │ │ └─ item: &Post_where #306 + │ └─ variant_2: struct #312 + │ └─ [none]: optional #311 + │ └─ item: &Post_where #306 + ├─ [id]: optional #301 │ └─ item: &_prisma_integer_filter_ex #35 - └─ [title]: optional #328 + └─ [title]: optional #302 └─ item: &_prisma_string_filter_ex #13 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__group_by Post out.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__group_by Post out.snap index c27cb995d..131b3f51c 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__group_by Post out.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__group_by Post out.snap @@ -2,25 +2,25 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/mod.rs expression: tp.print(out) --- -root: list 'Post_group' #446 -└─ item: struct #444 - ├─ [_avg]: struct 'Post_number_fields_float' #436 - │ └─ [id]: optional #434 - │ └─ item: float #433 - ├─ [_count]: struct 'Post_count_aggregate' #431 - │ ├─ [_all]: optional #429 - │ │ └─ item: integer #428 - │ ├─ [author]: optional #429 - │ │ └─ item: integer #428 - │ ├─ [id]: optional #429 - │ │ └─ item: integer #428 - │ └─ [title]: optional #429 - │ └─ item: integer #428 - ├─ [_max]: &Post_number_fields #437 - ├─ [_min]: &Post_number_fields #437 - ├─ [_sum]: struct 'Post_number_fields' #443 - │ └─ [id]: optional #441 - │ └─ item: integer #440 +root: list 'Post_group' #408 +└─ item: struct #406 + ├─ [_avg]: struct 'Post_number_fields_float' #398 + │ └─ [id]: optional #396 + │ └─ item: float #395 + ├─ [_count]: struct 'Post_count_aggregate' #393 + │ ├─ [_all]: optional #391 + │ │ └─ item: integer #390 + │ ├─ [author]: optional #391 + │ │ └─ item: integer #390 + │ ├─ [id]: optional #391 + │ │ └─ item: integer #390 + │ └─ [title]: optional #391 + │ └─ item: integer #390 + ├─ [_max]: &Post_number_fields #399 + ├─ [_min]: &Post_number_fields #399 + ├─ [_sum]: struct 'Post_number_fields' #405 + │ └─ [id]: optional #403 + │ └─ item: integer #402 ├─ [author]: &User #185 ├─ [id]: integer #183 └─ [title]: string #184 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__group_by User inp.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__group_by User inp.snap index 3114acce7..7b5136ac2 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__group_by User inp.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__group_by User inp.snap @@ -2,221 +2,131 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/mod.rs expression: tp.print(inp) --- -root: struct 'User_group_by_query_input' #302 -├─ [by]: list 'User_group_by' #257 -│ └─ item: string #255 enum{ '"id"', '"name"' } -├─ [cursor]: optional #253 -│ └─ item: union 'User_cursor' #252 -│ ├─ variant_0: struct #249 +root: struct 'User_group_by_query_input' #276 +├─ [by]: list 'User_group_by' #244 +│ └─ item: string #242 enum{ '"id"', '"name"' } +├─ [cursor]: optional #240 +│ └─ item: union 'User_cursor' #239 +│ ├─ variant_0: struct #236 │ │ └─ [id]: integer #175 -│ └─ variant_1: struct #250 +│ └─ variant_1: struct #237 │ └─ [name]: string #176 -├─ [having]: optional #300 -│ └─ item: union 'User_having' #299 -│ ├─ variant_0: struct 'User_where_with_aggregates' #291 -│ │ ├─ [id]: optional #260 +├─ [having]: optional #274 +│ └─ item: union 'User_having' #273 +│ ├─ variant_0: struct 'User_where_with_aggregates' #265 +│ │ ├─ [id]: optional #247 │ │ │ └─ item: &_prisma_integer_filter_with_aggregates_ex #114 -│ │ ├─ [name]: optional #261 +│ │ ├─ [name]: optional #248 │ │ │ └─ item: &_prisma_string_filter_ex #13 -│ │ └─ [posts]: optional #289 -│ │ └─ item: union #288 -│ │ ├─ variant_0: struct #283 -│ │ │ └─ [every]: optional #282 -│ │ │ └─ item: struct 'Post_where_with_aggregates_excluding_User' #281 -│ │ │ ├─ [author]: optional #279 -│ │ │ │ └─ item: struct 'User_where_with_aggregates_excluding_Post_and_User' #278 -│ │ │ │ ├─ [id]: optional #266 -│ │ │ │ │ └─ item: &_prisma_integer_filter_with_aggregates_ex #114 -│ │ │ │ ├─ [name]: optional #267 -│ │ │ │ │ └─ item: &_prisma_string_filter_ex #13 -│ │ │ │ └─ [posts]: optional #276 -│ │ │ │ └─ item: union #275 -│ │ │ │ ├─ variant_0: struct #270 -│ │ │ │ │ └─ [every]: optional #269 -│ │ │ │ │ └─ item: &Post_where_with_aggregates_excluding_User #268 -│ │ │ │ ├─ variant_1: struct #272 -│ │ │ │ │ └─ [some]: optional #271 -│ │ │ │ │ └─ item: &Post_where_with_aggregates_excluding_User #268 -│ │ │ │ └─ variant_2: struct #274 -│ │ │ │ └─ [none]: optional #273 -│ │ │ │ └─ item: &Post_where_with_aggregates_excluding_User #268 -│ │ │ ├─ [id]: optional #263 +│ │ └─ [posts]: optional #263 +│ │ └─ item: union #262 +│ │ ├─ variant_0: struct #257 +│ │ │ └─ [every]: optional #256 +│ │ │ └─ item: struct 'Post_where_with_aggregates_excluding_User' #255 +│ │ │ ├─ [author]: optional #253 +│ │ │ │ └─ item: &User_where_with_aggregates #252 +│ │ │ ├─ [id]: optional #250 │ │ │ │ └─ item: &_prisma_integer_filter_with_aggregates_ex #114 -│ │ │ └─ [title]: optional #264 +│ │ │ └─ [title]: optional #251 │ │ │ └─ item: &_prisma_string_filter_ex #13 -│ │ ├─ variant_1: struct #285 -│ │ │ └─ [some]: optional #284 -│ │ │ └─ item: struct 'Post_where_with_aggregates_excluding_User' #281 -│ │ │ ├─ [author]: optional #279 -│ │ │ │ └─ item: struct 'User_where_with_aggregates_excluding_Post_and_User' #278 -│ │ │ │ ├─ [id]: optional #266 -│ │ │ │ │ └─ item: &_prisma_integer_filter_with_aggregates_ex #114 -│ │ │ │ ├─ [name]: optional #267 -│ │ │ │ │ └─ item: &_prisma_string_filter_ex #13 -│ │ │ │ └─ [posts]: optional #276 -│ │ │ │ └─ item: union #275 -│ │ │ │ ├─ variant_0: struct #270 -│ │ │ │ │ └─ [every]: optional #269 -│ │ │ │ │ └─ item: &Post_where_with_aggregates_excluding_User #268 -│ │ │ │ ├─ variant_1: struct #272 -│ │ │ │ │ └─ [some]: optional #271 -│ │ │ │ │ └─ item: &Post_where_with_aggregates_excluding_User #268 -│ │ │ │ └─ variant_2: struct #274 -│ │ │ │ └─ [none]: optional #273 -│ │ │ │ └─ item: &Post_where_with_aggregates_excluding_User #268 -│ │ │ ├─ [id]: optional #263 +│ │ ├─ variant_1: struct #259 +│ │ │ └─ [some]: optional #258 +│ │ │ └─ item: struct 'Post_where_with_aggregates_excluding_User' #255 +│ │ │ ├─ [author]: optional #253 +│ │ │ │ └─ item: &User_where_with_aggregates #252 +│ │ │ ├─ [id]: optional #250 │ │ │ │ └─ item: &_prisma_integer_filter_with_aggregates_ex #114 -│ │ │ └─ [title]: optional #264 +│ │ │ └─ [title]: optional #251 │ │ │ └─ item: &_prisma_string_filter_ex #13 -│ │ └─ variant_2: struct #287 -│ │ └─ [none]: optional #286 -│ │ └─ item: struct 'Post_where_with_aggregates_excluding_User' #281 -│ │ ├─ [author]: optional #279 -│ │ │ └─ item: struct 'User_where_with_aggregates_excluding_Post_and_User' #278 -│ │ │ ├─ [id]: optional #266 -│ │ │ │ └─ item: &_prisma_integer_filter_with_aggregates_ex #114 -│ │ │ ├─ [name]: optional #267 -│ │ │ │ └─ item: &_prisma_string_filter_ex #13 -│ │ │ └─ [posts]: optional #276 -│ │ │ └─ item: union #275 -│ │ │ ├─ variant_0: struct #270 -│ │ │ │ └─ [every]: optional #269 -│ │ │ │ └─ item: &Post_where_with_aggregates_excluding_User #268 -│ │ │ ├─ variant_1: struct #272 -│ │ │ │ └─ [some]: optional #271 -│ │ │ │ └─ item: &Post_where_with_aggregates_excluding_User #268 -│ │ │ └─ variant_2: struct #274 -│ │ │ └─ [none]: optional #273 -│ │ │ └─ item: &Post_where_with_aggregates_excluding_User #268 -│ │ ├─ [id]: optional #263 +│ │ └─ variant_2: struct #261 +│ │ └─ [none]: optional #260 +│ │ └─ item: struct 'Post_where_with_aggregates_excluding_User' #255 +│ │ ├─ [author]: optional #253 +│ │ │ └─ item: &User_where_with_aggregates #252 +│ │ ├─ [id]: optional #250 │ │ │ └─ item: &_prisma_integer_filter_with_aggregates_ex #114 -│ │ └─ [title]: optional #264 +│ │ └─ [title]: optional #251 │ │ └─ item: &_prisma_string_filter_ex #13 -│ ├─ variant_1: struct #294 -│ │ └─ [AND]: list #293 -│ │ └─ item: &User_having #292 -│ ├─ variant_2: struct #296 -│ │ └─ [OR]: list #295 -│ │ └─ item: &User_having #292 -│ └─ variant_3: struct #297 -│ └─ [NOT]: &User_having #292 -├─ [orderBy]: optional #245 -│ └─ item: list 'User_order_by_with_aggregates' #244 -│ └─ item: struct #242 -│ ├─ [_avg]: optional #235 -│ │ └─ item: struct #234 +│ ├─ variant_1: struct #268 +│ │ └─ [AND]: list #267 +│ │ └─ item: &User_having #266 +│ ├─ variant_2: struct #270 +│ │ └─ [OR]: list #269 +│ │ └─ item: &User_having #266 +│ └─ variant_3: struct #271 +│ └─ [NOT]: &User_having #266 +├─ [orderBy]: optional #232 +│ └─ item: list 'User_order_by_with_aggregates' #231 +│ └─ item: struct #229 +│ ├─ [_avg]: optional #222 +│ │ └─ item: struct #221 │ │ └─ [id]: &_prisma_sort #64 -│ ├─ [_count]: optional #233 -│ │ └─ item: struct #232 +│ ├─ [_count]: optional #220 +│ │ └─ item: struct #219 │ │ ├─ [id]: &_prisma_sort #64 │ │ ├─ [name]: &_prisma_sort #64 │ │ └─ [posts]: &_prisma_sort #64 -│ ├─ [_max]: optional #235 -│ │ └─ item: struct #234 +│ ├─ [_max]: optional #222 +│ │ └─ item: struct #221 │ │ └─ [id]: &_prisma_sort #64 -│ ├─ [_min]: optional #235 -│ │ └─ item: struct #234 +│ ├─ [_min]: optional #222 +│ │ └─ item: struct #221 │ │ └─ [id]: &_prisma_sort #64 -│ ├─ [_sum]: optional #235 -│ │ └─ item: struct #234 +│ ├─ [_sum]: optional #222 +│ │ └─ item: struct #221 │ │ └─ [id]: &_prisma_sort #64 │ ├─ [id]: &_prisma_sort #64 │ ├─ [name]: &_prisma_sort #64 -│ └─ [posts]: optional '_prisma_sort_by_aggregates' #241 -│ └─ item: struct #239 +│ └─ [posts]: optional '_prisma_sort_by_aggregates' #228 +│ └─ item: struct #226 │ ├─ [_avg]: &_prisma_sort #64 │ ├─ [_count]: &_prisma_sort #64 │ ├─ [_max]: &_prisma_sort #64 │ ├─ [_min]: &_prisma_sort #64 │ └─ [_sum]: &_prisma_sort #64 -├─ [skip]: optional #247 +├─ [skip]: optional #234 │ └─ item: &_skip #94 -├─ [take]: optional #246 +├─ [take]: optional #233 │ └─ item: &_take #90 -└─ [where]: optional #229 - └─ item: struct 'User_query_where_input' #228 - ├─ [AND]: optional #225 - │ └─ item: list #224 - │ └─ item: &User_query_where_input #223 - ├─ [NOT]: optional #226 - │ └─ item: &User_query_where_input #223 - ├─ [OR]: optional #225 - │ └─ item: list #224 - │ └─ item: &User_query_where_input #223 +└─ [where]: optional #216 + └─ item: struct 'User_query_where_input' #215 + ├─ [AND]: optional #212 + │ └─ item: list #211 + │ └─ item: &User_query_where_input #210 + ├─ [NOT]: optional #213 + │ └─ item: &User_query_where_input #210 + ├─ [OR]: optional #212 + │ └─ item: list #211 + │ └─ item: &User_query_where_input #210 ├─ [id]: optional #191 │ └─ item: &_prisma_integer_filter_ex #35 ├─ [name]: optional #192 │ └─ item: &_prisma_string_filter_ex #13 - └─ [posts]: optional #220 - └─ item: union #219 - ├─ variant_0: struct #214 - │ └─ [every]: optional #213 - │ └─ item: struct 'Post_where_excluding_User' #212 - │ ├─ [author]: optional #210 - │ │ └─ item: struct 'User_where_excluding_Post_and_User' #209 - │ │ ├─ [id]: optional #197 - │ │ │ └─ item: &_prisma_integer_filter_ex #35 - │ │ ├─ [name]: optional #198 - │ │ │ └─ item: &_prisma_string_filter_ex #13 - │ │ └─ [posts]: optional #207 - │ │ └─ item: union #206 - │ │ ├─ variant_0: struct #201 - │ │ │ └─ [every]: optional #200 - │ │ │ └─ item: &Post_where_excluding_User #199 - │ │ ├─ variant_1: struct #203 - │ │ │ └─ [some]: optional #202 - │ │ │ └─ item: &Post_where_excluding_User #199 - │ │ └─ variant_2: struct #205 - │ │ └─ [none]: optional #204 - │ │ └─ item: &Post_where_excluding_User #199 + └─ [posts]: optional #207 + └─ item: union #206 + ├─ variant_0: struct #201 + │ └─ [every]: optional #200 + │ └─ item: struct 'Post_where_excluding_User' #199 + │ ├─ [author]: optional #197 + │ │ └─ item: &User_where #196 │ ├─ [id]: optional #194 │ │ └─ item: &_prisma_integer_filter_ex #35 │ └─ [title]: optional #195 │ └─ item: &_prisma_string_filter_ex #13 - ├─ variant_1: struct #216 - │ └─ [some]: optional #215 - │ └─ item: struct 'Post_where_excluding_User' #212 - │ ├─ [author]: optional #210 - │ │ └─ item: struct 'User_where_excluding_Post_and_User' #209 - │ │ ├─ [id]: optional #197 - │ │ │ └─ item: &_prisma_integer_filter_ex #35 - │ │ ├─ [name]: optional #198 - │ │ │ └─ item: &_prisma_string_filter_ex #13 - │ │ └─ [posts]: optional #207 - │ │ └─ item: union #206 - │ │ ├─ variant_0: struct #201 - │ │ │ └─ [every]: optional #200 - │ │ │ └─ item: &Post_where_excluding_User #199 - │ │ ├─ variant_1: struct #203 - │ │ │ └─ [some]: optional #202 - │ │ │ └─ item: &Post_where_excluding_User #199 - │ │ └─ variant_2: struct #205 - │ │ └─ [none]: optional #204 - │ │ └─ item: &Post_where_excluding_User #199 + ├─ variant_1: struct #203 + │ └─ [some]: optional #202 + │ └─ item: struct 'Post_where_excluding_User' #199 + │ ├─ [author]: optional #197 + │ │ └─ item: &User_where #196 │ ├─ [id]: optional #194 │ │ └─ item: &_prisma_integer_filter_ex #35 │ └─ [title]: optional #195 │ └─ item: &_prisma_string_filter_ex #13 - └─ variant_2: struct #218 - └─ [none]: optional #217 - └─ item: struct 'Post_where_excluding_User' #212 - ├─ [author]: optional #210 - │ └─ item: struct 'User_where_excluding_Post_and_User' #209 - │ ├─ [id]: optional #197 - │ │ └─ item: &_prisma_integer_filter_ex #35 - │ ├─ [name]: optional #198 - │ │ └─ item: &_prisma_string_filter_ex #13 - │ └─ [posts]: optional #207 - │ └─ item: union #206 - │ ├─ variant_0: struct #201 - │ │ └─ [every]: optional #200 - │ │ └─ item: &Post_where_excluding_User #199 - │ ├─ variant_1: struct #203 - │ │ └─ [some]: optional #202 - │ │ └─ item: &Post_where_excluding_User #199 - │ └─ variant_2: struct #205 - │ └─ [none]: optional #204 - │ └─ item: &Post_where_excluding_User #199 + └─ variant_2: struct #205 + └─ [none]: optional #204 + └─ item: struct 'Post_where_excluding_User' #199 + ├─ [author]: optional #197 + │ └─ item: &User_where #196 ├─ [id]: optional #194 │ └─ item: &_prisma_integer_filter_ex #35 └─ [title]: optional #195 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__group_by User out.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__group_by User out.snap index 8c415a746..b0018c0ae 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__group_by User out.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__group_by User out.snap @@ -2,25 +2,25 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/mod.rs expression: tp.print(out) --- -root: list 'User_group' #323 -└─ item: struct #321 - ├─ [_avg]: struct 'User_number_fields_float' #313 - │ └─ [id]: optional #311 - │ └─ item: float #310 - ├─ [_count]: struct 'User_count_aggregate' #308 - │ ├─ [_all]: optional #306 - │ │ └─ item: integer #305 - │ ├─ [id]: optional #306 - │ │ └─ item: integer #305 - │ ├─ [name]: optional #306 - │ │ └─ item: integer #305 - │ └─ [posts]: optional #306 - │ └─ item: integer #305 - ├─ [_max]: &User_number_fields #314 - ├─ [_min]: &User_number_fields #314 - ├─ [_sum]: struct 'User_number_fields' #320 - │ └─ [id]: optional #318 - │ └─ item: integer #317 +root: list 'User_group' #297 +└─ item: struct #295 + ├─ [_avg]: struct 'User_number_fields_float' #287 + │ └─ [id]: optional #285 + │ └─ item: float #284 + ├─ [_count]: struct 'User_count_aggregate' #282 + │ ├─ [_all]: optional #280 + │ │ └─ item: integer #279 + │ ├─ [id]: optional #280 + │ │ └─ item: integer #279 + │ ├─ [name]: optional #280 + │ │ └─ item: integer #279 + │ └─ [posts]: optional #280 + │ └─ item: integer #279 + ├─ [_max]: &User_number_fields #288 + ├─ [_min]: &User_number_fields #288 + ├─ [_sum]: struct 'User_number_fields' #294 + │ └─ [id]: optional #292 + │ └─ item: integer #291 ├─ [id]: integer #175 ├─ [name]: string #176 └─ [posts]: list #178 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__update_many Post inp.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__update_many Post inp.snap index 6ddf21df8..27e5a852a 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__update_many Post inp.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__update_many Post inp.snap @@ -2,66 +2,66 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/mod.rs expression: tp.print(inp) --- -root: struct #247 -├─ [data]: struct 'Post_update_input' #238 -│ ├─ [author]: optional #236 -│ │ └─ item: union #235 -│ │ ├─ variant_0: struct #219 -│ │ │ └─ [create]: struct 'User_create_input_excluding_rel_Post_User' #217 +root: struct #234 +├─ [data]: struct 'Post_update_input' #225 +│ ├─ [author]: optional #223 +│ │ └─ item: union #222 +│ │ ├─ variant_0: struct #206 +│ │ │ └─ [create]: struct 'User_create_input_excluding_rel_Post_User' #204 │ │ │ ├─ [id]: integer #81 │ │ │ └─ [name]: string #82 -│ │ ├─ variant_1: struct #220 +│ │ ├─ variant_1: struct #207 │ │ │ └─ [connect]: &User_where #164 -│ │ ├─ variant_2: struct #221 -│ │ │ └─ [connectOrCreate]: struct #218 -│ │ │ ├─ [create]: struct 'User_create_input_excluding_rel_Post_User' #217 +│ │ ├─ variant_2: struct #208 +│ │ │ └─ [connectOrCreate]: struct #205 +│ │ │ ├─ [create]: struct 'User_create_input_excluding_rel_Post_User' #204 │ │ │ │ ├─ [id]: integer #81 │ │ │ │ └─ [name]: string #82 │ │ │ └─ [where]: &User_where #164 -│ │ └─ variant_3: struct #234 -│ │ └─ [update]: struct 'User_update_input_excluding_rel_Post_User' #233 -│ │ ├─ [id]: optional #228 -│ │ │ └─ item: union #227 +│ │ └─ variant_3: struct #221 +│ │ └─ [update]: struct 'User_update_input_excluding_rel_Post_User' #220 +│ │ ├─ [id]: optional #215 +│ │ │ └─ item: union #214 │ │ │ ├─ variant_0: integer #81 -│ │ │ ├─ variant_1: struct #223 +│ │ │ ├─ variant_1: struct #210 │ │ │ │ └─ [set]: integer #81 -│ │ │ ├─ variant_2: struct #224 +│ │ │ ├─ variant_2: struct #211 │ │ │ │ └─ [multiply]: integer #81 -│ │ │ ├─ variant_3: struct #225 +│ │ │ ├─ variant_3: struct #212 │ │ │ │ └─ [decrement]: integer #81 -│ │ │ └─ variant_4: struct #226 +│ │ │ └─ variant_4: struct #213 │ │ │ └─ [increment]: integer #81 -│ │ └─ [name]: optional #231 -│ │ └─ item: union #230 +│ │ └─ [name]: optional #218 +│ │ └─ item: union #217 │ │ ├─ variant_0: string #82 -│ │ └─ variant_1: struct #229 +│ │ └─ variant_1: struct #216 │ │ └─ [set]: string #82 -│ ├─ [id]: optional #211 -│ │ └─ item: union #210 +│ ├─ [id]: optional #198 +│ │ └─ item: union #197 │ │ ├─ variant_0: integer #89 -│ │ ├─ variant_1: struct #206 +│ │ ├─ variant_1: struct #193 │ │ │ └─ [set]: integer #89 -│ │ ├─ variant_2: struct #207 +│ │ ├─ variant_2: struct #194 │ │ │ └─ [multiply]: integer #89 -│ │ ├─ variant_3: struct #208 +│ │ ├─ variant_3: struct #195 │ │ │ └─ [decrement]: integer #89 -│ │ └─ variant_4: struct #209 +│ │ └─ variant_4: struct #196 │ │ └─ [increment]: integer #89 -│ └─ [title]: optional #214 -│ └─ item: union #213 +│ └─ [title]: optional #201 +│ └─ item: union #200 │ ├─ variant_0: string #90 -│ └─ variant_1: struct #212 +│ └─ variant_1: struct #199 │ └─ [set]: string #90 -└─ [where]: optional #246 - └─ item: struct 'Post_query_where_input' #245 - ├─ [AND]: optional #242 - │ └─ item: list #241 - │ └─ item: &Post_query_where_input #240 - ├─ [NOT]: optional #243 - │ └─ item: &Post_query_where_input #240 - ├─ [OR]: optional #242 - │ └─ item: list #241 - │ └─ item: &Post_query_where_input #240 +└─ [where]: optional #233 + └─ item: struct 'Post_query_where_input' #232 + ├─ [AND]: optional #229 + │ └─ item: list #228 + │ └─ item: &Post_query_where_input #227 + ├─ [NOT]: optional #230 + │ └─ item: &Post_query_where_input #227 + ├─ [OR]: optional #229 + │ └─ item: list #228 + │ └─ item: &Post_query_where_input #227 ├─ [author]: optional #127 │ └─ item: struct 'User_where_excluding_Post' #126 │ ├─ [id]: optional #114 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__update_many User inp.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__update_many User inp.snap index bb1469af5..152e28386 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__update_many User inp.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__update_many User inp.snap @@ -2,7 +2,7 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/mod.rs expression: tp.print(inp) --- -root: struct #204 +root: struct #191 ├─ [data]: struct 'User_update_input' #162 │ ├─ [id]: optional #100 │ │ └─ item: union #99 @@ -285,90 +285,45 @@ root: struct #204 │ ├─ [id]: optional #105 │ │ └─ item: integer #89 │ └─ [title]: string #90 -└─ [where]: optional #203 - └─ item: struct 'User_query_where_input' #202 - ├─ [AND]: optional #199 - │ └─ item: list #198 - │ └─ item: &User_query_where_input #197 - ├─ [NOT]: optional #200 - │ └─ item: &User_query_where_input #197 - ├─ [OR]: optional #199 - │ └─ item: list #198 - │ └─ item: &User_query_where_input #197 +└─ [where]: optional #190 + └─ item: struct 'User_query_where_input' #189 + ├─ [AND]: optional #186 + │ └─ item: list #185 + │ └─ item: &User_query_where_input #184 + ├─ [NOT]: optional #187 + │ └─ item: &User_query_where_input #184 + ├─ [OR]: optional #186 + │ └─ item: list #185 + │ └─ item: &User_query_where_input #184 ├─ [id]: optional #165 │ └─ item: &_prisma_integer_filter_ex #52 ├─ [name]: optional #166 │ └─ item: &_prisma_string_filter_ex #30 - └─ [posts]: optional #194 - └─ item: union #193 - ├─ variant_0: struct #188 - │ └─ [every]: optional #187 - │ └─ item: struct 'Post_where_excluding_User' #186 - │ ├─ [author]: optional #184 - │ │ └─ item: struct 'User_where_excluding_Post_and_User' #183 - │ │ ├─ [id]: optional #171 - │ │ │ └─ item: &_prisma_integer_filter_ex #52 - │ │ ├─ [name]: optional #172 - │ │ │ └─ item: &_prisma_string_filter_ex #30 - │ │ └─ [posts]: optional #181 - │ │ └─ item: union #180 - │ │ ├─ variant_0: struct #175 - │ │ │ └─ [every]: optional #174 - │ │ │ └─ item: &Post_where_excluding_User #173 - │ │ ├─ variant_1: struct #177 - │ │ │ └─ [some]: optional #176 - │ │ │ └─ item: &Post_where_excluding_User #173 - │ │ └─ variant_2: struct #179 - │ │ └─ [none]: optional #178 - │ │ └─ item: &Post_where_excluding_User #173 + └─ [posts]: optional #181 + └─ item: union #180 + ├─ variant_0: struct #175 + │ └─ [every]: optional #174 + │ └─ item: struct 'Post_where_excluding_User' #173 + │ ├─ [author]: optional #171 + │ │ └─ item: &User_where #170 │ ├─ [id]: optional #168 │ │ └─ item: &_prisma_integer_filter_ex #52 │ └─ [title]: optional #169 │ └─ item: &_prisma_string_filter_ex #30 - ├─ variant_1: struct #190 - │ └─ [some]: optional #189 - │ └─ item: struct 'Post_where_excluding_User' #186 - │ ├─ [author]: optional #184 - │ │ └─ item: struct 'User_where_excluding_Post_and_User' #183 - │ │ ├─ [id]: optional #171 - │ │ │ └─ item: &_prisma_integer_filter_ex #52 - │ │ ├─ [name]: optional #172 - │ │ │ └─ item: &_prisma_string_filter_ex #30 - │ │ └─ [posts]: optional #181 - │ │ └─ item: union #180 - │ │ ├─ variant_0: struct #175 - │ │ │ └─ [every]: optional #174 - │ │ │ └─ item: &Post_where_excluding_User #173 - │ │ ├─ variant_1: struct #177 - │ │ │ └─ [some]: optional #176 - │ │ │ └─ item: &Post_where_excluding_User #173 - │ │ └─ variant_2: struct #179 - │ │ └─ [none]: optional #178 - │ │ └─ item: &Post_where_excluding_User #173 + ├─ variant_1: struct #177 + │ └─ [some]: optional #176 + │ └─ item: struct 'Post_where_excluding_User' #173 + │ ├─ [author]: optional #171 + │ │ └─ item: &User_where #170 │ ├─ [id]: optional #168 │ │ └─ item: &_prisma_integer_filter_ex #52 │ └─ [title]: optional #169 │ └─ item: &_prisma_string_filter_ex #30 - └─ variant_2: struct #192 - └─ [none]: optional #191 - └─ item: struct 'Post_where_excluding_User' #186 - ├─ [author]: optional #184 - │ └─ item: struct 'User_where_excluding_Post_and_User' #183 - │ ├─ [id]: optional #171 - │ │ └─ item: &_prisma_integer_filter_ex #52 - │ ├─ [name]: optional #172 - │ │ └─ item: &_prisma_string_filter_ex #30 - │ └─ [posts]: optional #181 - │ └─ item: union #180 - │ ├─ variant_0: struct #175 - │ │ └─ [every]: optional #174 - │ │ └─ item: &Post_where_excluding_User #173 - │ ├─ variant_1: struct #177 - │ │ └─ [some]: optional #176 - │ │ └─ item: &Post_where_excluding_User #173 - │ └─ variant_2: struct #179 - │ └─ [none]: optional #178 - │ └─ item: &Post_where_excluding_User #173 + └─ variant_2: struct #179 + └─ [none]: optional #178 + └─ item: struct 'Post_where_excluding_User' #173 + ├─ [author]: optional #171 + │ └─ item: &User_where #170 ├─ [id]: optional #168 │ └─ item: &_prisma_integer_filter_ex #52 └─ [title]: optional #169 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__update_one Post inp.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__update_one Post inp.snap index 195331f77..0f250e717 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__update_one Post inp.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__update_one Post inp.snap @@ -2,65 +2,65 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/mod.rs expression: tp.print(inp) --- -root: struct #244 -├─ [data]: struct 'Post_update_input' #236 -│ ├─ [author]: optional #234 -│ │ └─ item: union #233 -│ │ ├─ variant_0: struct #217 -│ │ │ └─ [create]: struct 'User_create_input_excluding_rel_Post_User' #215 +root: struct #231 +├─ [data]: struct 'Post_update_input' #223 +│ ├─ [author]: optional #221 +│ │ └─ item: union #220 +│ │ ├─ variant_0: struct #204 +│ │ │ └─ [create]: struct 'User_create_input_excluding_rel_Post_User' #202 │ │ │ ├─ [id]: integer #80 │ │ │ └─ [name]: string #81 -│ │ ├─ variant_1: struct #218 +│ │ ├─ variant_1: struct #205 │ │ │ └─ [connect]: &User_where #163 -│ │ ├─ variant_2: struct #219 -│ │ │ └─ [connectOrCreate]: struct #216 -│ │ │ ├─ [create]: struct 'User_create_input_excluding_rel_Post_User' #215 +│ │ ├─ variant_2: struct #206 +│ │ │ └─ [connectOrCreate]: struct #203 +│ │ │ ├─ [create]: struct 'User_create_input_excluding_rel_Post_User' #202 │ │ │ │ ├─ [id]: integer #80 │ │ │ │ └─ [name]: string #81 │ │ │ └─ [where]: &User_where #163 -│ │ └─ variant_3: struct #232 -│ │ └─ [update]: struct 'User_update_input_excluding_rel_Post_User' #231 -│ │ ├─ [id]: optional #226 -│ │ │ └─ item: union #225 +│ │ └─ variant_3: struct #219 +│ │ └─ [update]: struct 'User_update_input_excluding_rel_Post_User' #218 +│ │ ├─ [id]: optional #213 +│ │ │ └─ item: union #212 │ │ │ ├─ variant_0: integer #80 -│ │ │ ├─ variant_1: struct #221 +│ │ │ ├─ variant_1: struct #208 │ │ │ │ └─ [set]: integer #80 -│ │ │ ├─ variant_2: struct #222 +│ │ │ ├─ variant_2: struct #209 │ │ │ │ └─ [multiply]: integer #80 -│ │ │ ├─ variant_3: struct #223 +│ │ │ ├─ variant_3: struct #210 │ │ │ │ └─ [decrement]: integer #80 -│ │ │ └─ variant_4: struct #224 +│ │ │ └─ variant_4: struct #211 │ │ │ └─ [increment]: integer #80 -│ │ └─ [name]: optional #229 -│ │ └─ item: union #228 +│ │ └─ [name]: optional #216 +│ │ └─ item: union #215 │ │ ├─ variant_0: string #81 -│ │ └─ variant_1: struct #227 +│ │ └─ variant_1: struct #214 │ │ └─ [set]: string #81 -│ ├─ [id]: optional #209 -│ │ └─ item: union #208 +│ ├─ [id]: optional #196 +│ │ └─ item: union #195 │ │ ├─ variant_0: integer #88 -│ │ ├─ variant_1: struct #204 +│ │ ├─ variant_1: struct #191 │ │ │ └─ [set]: integer #88 -│ │ ├─ variant_2: struct #205 +│ │ ├─ variant_2: struct #192 │ │ │ └─ [multiply]: integer #88 -│ │ ├─ variant_3: struct #206 +│ │ ├─ variant_3: struct #193 │ │ │ └─ [decrement]: integer #88 -│ │ └─ variant_4: struct #207 +│ │ └─ variant_4: struct #194 │ │ └─ [increment]: integer #88 -│ └─ [title]: optional #212 -│ └─ item: union #211 +│ └─ [title]: optional #199 +│ └─ item: union #198 │ ├─ variant_0: string #89 -│ └─ variant_1: struct #210 +│ └─ variant_1: struct #197 │ └─ [set]: string #89 -└─ [where]: struct 'Post_query_where_unique_input' #243 - ├─ [AND]: optional #240 - │ └─ item: list #239 - │ └─ item: &Post_query_where_unique_input #238 - ├─ [NOT]: optional #241 - │ └─ item: &Post_query_where_unique_input #238 - ├─ [OR]: optional #240 - │ └─ item: list #239 - │ └─ item: &Post_query_where_unique_input #238 +└─ [where]: struct 'Post_query_where_unique_input' #230 + ├─ [AND]: optional #227 + │ └─ item: list #226 + │ └─ item: &Post_query_where_unique_input #225 + ├─ [NOT]: optional #228 + │ └─ item: &Post_query_where_unique_input #225 + ├─ [OR]: optional #227 + │ └─ item: list #226 + │ └─ item: &Post_query_where_unique_input #225 ├─ [author]: optional #126 │ └─ item: struct 'User_where_excluding_Post' #125 │ ├─ [id]: optional #113 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__update_one User inp.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__update_one User inp.snap index e16f249b2..b96b539f2 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__update_one User inp.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__test__update_one User inp.snap @@ -2,7 +2,7 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/mod.rs expression: tp.print(inp) --- -root: struct #202 +root: struct #189 ├─ [data]: struct 'User_update_input' #161 │ ├─ [id]: optional #99 │ │ └─ item: union #98 @@ -285,89 +285,44 @@ root: struct #202 │ ├─ [id]: optional #104 │ │ └─ item: integer #88 │ └─ [title]: string #89 -└─ [where]: struct 'User_query_where_unique_input' #201 - ├─ [AND]: optional #198 - │ └─ item: list #197 - │ └─ item: &User_query_where_unique_input #196 - ├─ [NOT]: optional #199 - │ └─ item: &User_query_where_unique_input #196 - ├─ [OR]: optional #198 - │ └─ item: list #197 - │ └─ item: &User_query_where_unique_input #196 +└─ [where]: struct 'User_query_where_unique_input' #188 + ├─ [AND]: optional #185 + │ └─ item: list #184 + │ └─ item: &User_query_where_unique_input #183 + ├─ [NOT]: optional #186 + │ └─ item: &User_query_where_unique_input #183 + ├─ [OR]: optional #185 + │ └─ item: list #184 + │ └─ item: &User_query_where_unique_input #183 ├─ [id]: optional #164 │ └─ item: &_prisma_integer_filter_ex #52 ├─ [name]: optional #165 │ └─ item: &_prisma_string_filter_ex #30 - └─ [posts]: optional #193 - └─ item: union #192 - ├─ variant_0: struct #187 - │ └─ [every]: optional #186 - │ └─ item: struct 'Post_where_excluding_User' #185 - │ ├─ [author]: optional #183 - │ │ └─ item: struct 'User_where_excluding_Post_and_User' #182 - │ │ ├─ [id]: optional #170 - │ │ │ └─ item: &_prisma_integer_filter_ex #52 - │ │ ├─ [name]: optional #171 - │ │ │ └─ item: &_prisma_string_filter_ex #30 - │ │ └─ [posts]: optional #180 - │ │ └─ item: union #179 - │ │ ├─ variant_0: struct #174 - │ │ │ └─ [every]: optional #173 - │ │ │ └─ item: &Post_where_excluding_User #172 - │ │ ├─ variant_1: struct #176 - │ │ │ └─ [some]: optional #175 - │ │ │ └─ item: &Post_where_excluding_User #172 - │ │ └─ variant_2: struct #178 - │ │ └─ [none]: optional #177 - │ │ └─ item: &Post_where_excluding_User #172 + └─ [posts]: optional #180 + └─ item: union #179 + ├─ variant_0: struct #174 + │ └─ [every]: optional #173 + │ └─ item: struct 'Post_where_excluding_User' #172 + │ ├─ [author]: optional #170 + │ │ └─ item: &User_where #169 │ ├─ [id]: optional #167 │ │ └─ item: &_prisma_integer_filter_ex #52 │ └─ [title]: optional #168 │ └─ item: &_prisma_string_filter_ex #30 - ├─ variant_1: struct #189 - │ └─ [some]: optional #188 - │ └─ item: struct 'Post_where_excluding_User' #185 - │ ├─ [author]: optional #183 - │ │ └─ item: struct 'User_where_excluding_Post_and_User' #182 - │ │ ├─ [id]: optional #170 - │ │ │ └─ item: &_prisma_integer_filter_ex #52 - │ │ ├─ [name]: optional #171 - │ │ │ └─ item: &_prisma_string_filter_ex #30 - │ │ └─ [posts]: optional #180 - │ │ └─ item: union #179 - │ │ ├─ variant_0: struct #174 - │ │ │ └─ [every]: optional #173 - │ │ │ └─ item: &Post_where_excluding_User #172 - │ │ ├─ variant_1: struct #176 - │ │ │ └─ [some]: optional #175 - │ │ │ └─ item: &Post_where_excluding_User #172 - │ │ └─ variant_2: struct #178 - │ │ └─ [none]: optional #177 - │ │ └─ item: &Post_where_excluding_User #172 + ├─ variant_1: struct #176 + │ └─ [some]: optional #175 + │ └─ item: struct 'Post_where_excluding_User' #172 + │ ├─ [author]: optional #170 + │ │ └─ item: &User_where #169 │ ├─ [id]: optional #167 │ │ └─ item: &_prisma_integer_filter_ex #52 │ └─ [title]: optional #168 │ └─ item: &_prisma_string_filter_ex #30 - └─ variant_2: struct #191 - └─ [none]: optional #190 - └─ item: struct 'Post_where_excluding_User' #185 - ├─ [author]: optional #183 - │ └─ item: struct 'User_where_excluding_Post_and_User' #182 - │ ├─ [id]: optional #170 - │ │ └─ item: &_prisma_integer_filter_ex #52 - │ ├─ [name]: optional #171 - │ │ └─ item: &_prisma_string_filter_ex #30 - │ └─ [posts]: optional #180 - │ └─ item: union #179 - │ ├─ variant_0: struct #174 - │ │ └─ [every]: optional #173 - │ │ └─ item: &Post_where_excluding_User #172 - │ ├─ variant_1: struct #176 - │ │ └─ [some]: optional #175 - │ │ └─ item: &Post_where_excluding_User #172 - │ └─ variant_2: struct #178 - │ └─ [none]: optional #177 - │ └─ item: &Post_where_excluding_User #172 + └─ variant_2: struct #178 + └─ [none]: optional #177 + └─ item: struct 'Post_where_excluding_User' #172 + ├─ [author]: optional #170 + │ └─ item: &User_where #169 ├─ [id]: optional #167 │ └─ item: &_prisma_integer_filter_ex #52 └─ [title]: optional #168 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__where___test__where Post.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__where___test__where Post.snap index a8e5e2db9..82194dfe3 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__where___test__where Post.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__where___test__where Post.snap @@ -2,43 +2,25 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/where_.rs expression: "tree::print(context.generate(&Where::new(post))?)" --- -root: struct 'Post_where' #125 -├── [author]: optional #123 -│ └── item: struct 'User_where_excluding_Post' #122 -│ ├── [id]: optional #104 +root: struct 'Post_where' #106 +├── [author]: optional #104 +│ └── item: struct 'User_where_excluding_Post' #103 +│ ├── [id]: optional #91 │ │ └── item: &_prisma_integer_filter_ex #33 -│ ├── [name]: optional #105 +│ ├── [name]: optional #92 │ │ └── item: &_prisma_string_filter_ex #11 -│ └── [posts]: optional #120 -│ └── item: union #119 -│ ├── variant_0: struct #114 -│ │ └── [every]: optional #113 -│ │ └── item: struct 'Post_where_excluding_User_and_Post' #112 -│ │ ├── [author]: optional #110 -│ │ │ └── item: &User_where_excluding_Post #109 -│ │ ├── [id]: optional #107 -│ │ │ └── item: &_prisma_integer_filter_ex #33 -│ │ └── [title]: optional #108 -│ │ └── item: &_prisma_string_filter_ex #11 -│ ├── variant_1: struct #116 -│ │ └── [some]: optional #115 -│ │ └── item: struct 'Post_where_excluding_User_and_Post' #112 -│ │ ├── [author]: optional #110 -│ │ │ └── item: &User_where_excluding_Post #109 -│ │ ├── [id]: optional #107 -│ │ │ └── item: &_prisma_integer_filter_ex #33 -│ │ └── [title]: optional #108 -│ │ └── item: &_prisma_string_filter_ex #11 -│ └── variant_2: struct #118 -│ └── [none]: optional #117 -│ └── item: struct 'Post_where_excluding_User_and_Post' #112 -│ ├── [author]: optional #110 -│ │ └── item: &User_where_excluding_Post #109 -│ ├── [id]: optional #107 -│ │ └── item: &_prisma_integer_filter_ex #33 -│ └── [title]: optional #108 -│ └── item: &_prisma_string_filter_ex #11 -├── [id]: optional #101 +│ └── [posts]: optional #101 +│ └── item: union #100 +│ ├── variant_0: struct #95 +│ │ └── [every]: optional #94 +│ │ └── item: &Post_where #93 +│ ├── variant_1: struct #97 +│ │ └── [some]: optional #96 +│ │ └── item: &Post_where #93 +│ └── variant_2: struct #99 +│ └── [none]: optional #98 +│ └── item: &Post_where #93 +├── [id]: optional #88 │ └── item: &_prisma_integer_filter_ex #33 -└── [title]: optional #102 +└── [title]: optional #89 └── item: &_prisma_string_filter_ex #11 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__where___test__where User.snap b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__where___test__where User.snap index 8a2ccf781..fabdf798e 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__where___test__where User.snap +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/snapshots/typegraph_core__runtimes__prisma__type_generation__where___test__where User.snap @@ -2,81 +2,36 @@ source: src/typegraph/core/src/runtimes/prisma/type_generation/where_.rs expression: "tree::print(context.generate(&Where::new(user))?)" --- -root: struct 'User_where' #99 +root: struct 'User_where' #86 ├── [id]: optional #68 │ └── item: &_prisma_integer_filter_ex #33 ├── [name]: optional #69 │ └── item: &_prisma_string_filter_ex #11 -└── [posts]: optional #97 - └── item: union #96 - ├── variant_0: struct #91 - │ └── [every]: optional #90 - │ └── item: struct 'Post_where_excluding_User' #89 - │ ├── [author]: optional #87 - │ │ └── item: struct 'User_where_excluding_Post_and_User' #86 - │ │ ├── [id]: optional #74 - │ │ │ └── item: &_prisma_integer_filter_ex #33 - │ │ ├── [name]: optional #75 - │ │ │ └── item: &_prisma_string_filter_ex #11 - │ │ └── [posts]: optional #84 - │ │ └── item: union #83 - │ │ ├── variant_0: struct #78 - │ │ │ └── [every]: optional #77 - │ │ │ └── item: &Post_where_excluding_User #76 - │ │ ├── variant_1: struct #80 - │ │ │ └── [some]: optional #79 - │ │ │ └── item: &Post_where_excluding_User #76 - │ │ └── variant_2: struct #82 - │ │ └── [none]: optional #81 - │ │ └── item: &Post_where_excluding_User #76 +└── [posts]: optional #84 + └── item: union #83 + ├── variant_0: struct #78 + │ └── [every]: optional #77 + │ └── item: struct 'Post_where_excluding_User' #76 + │ ├── [author]: optional #74 + │ │ └── item: &User_where #73 │ ├── [id]: optional #71 │ │ └── item: &_prisma_integer_filter_ex #33 │ └── [title]: optional #72 │ └── item: &_prisma_string_filter_ex #11 - ├── variant_1: struct #93 - │ └── [some]: optional #92 - │ └── item: struct 'Post_where_excluding_User' #89 - │ ├── [author]: optional #87 - │ │ └── item: struct 'User_where_excluding_Post_and_User' #86 - │ │ ├── [id]: optional #74 - │ │ │ └── item: &_prisma_integer_filter_ex #33 - │ │ ├── [name]: optional #75 - │ │ │ └── item: &_prisma_string_filter_ex #11 - │ │ └── [posts]: optional #84 - │ │ └── item: union #83 - │ │ ├── variant_0: struct #78 - │ │ │ └── [every]: optional #77 - │ │ │ └── item: &Post_where_excluding_User #76 - │ │ ├── variant_1: struct #80 - │ │ │ └── [some]: optional #79 - │ │ │ └── item: &Post_where_excluding_User #76 - │ │ └── variant_2: struct #82 - │ │ └── [none]: optional #81 - │ │ └── item: &Post_where_excluding_User #76 + ├── variant_1: struct #80 + │ └── [some]: optional #79 + │ └── item: struct 'Post_where_excluding_User' #76 + │ ├── [author]: optional #74 + │ │ └── item: &User_where #73 │ ├── [id]: optional #71 │ │ └── item: &_prisma_integer_filter_ex #33 │ └── [title]: optional #72 │ └── item: &_prisma_string_filter_ex #11 - └── variant_2: struct #95 - └── [none]: optional #94 - └── item: struct 'Post_where_excluding_User' #89 - ├── [author]: optional #87 - │ └── item: struct 'User_where_excluding_Post_and_User' #86 - │ ├── [id]: optional #74 - │ │ └── item: &_prisma_integer_filter_ex #33 - │ ├── [name]: optional #75 - │ │ └── item: &_prisma_string_filter_ex #11 - │ └── [posts]: optional #84 - │ └── item: union #83 - │ ├── variant_0: struct #78 - │ │ └── [every]: optional #77 - │ │ └── item: &Post_where_excluding_User #76 - │ ├── variant_1: struct #80 - │ │ └── [some]: optional #79 - │ │ └── item: &Post_where_excluding_User #76 - │ └── variant_2: struct #82 - │ └── [none]: optional #81 - │ └── item: &Post_where_excluding_User #76 + └── variant_2: struct #82 + └── [none]: optional #81 + └── item: struct 'Post_where_excluding_User' #76 + ├── [author]: optional #74 + │ └── item: &User_where #73 ├── [id]: optional #71 │ └── item: &_prisma_integer_filter_ex #33 └── [title]: optional #72 diff --git a/src/typegraph/core/src/runtimes/prisma/type_generation/where_.rs b/src/typegraph/core/src/runtimes/prisma/type_generation/where_.rs index b1f83b19a..1b8c6bb4d 100644 --- a/src/typegraph/core/src/runtimes/prisma/type_generation/where_.rs +++ b/src/typegraph/core/src/runtimes/prisma/type_generation/where_.rs @@ -17,7 +17,7 @@ use super::TypeGen; pub struct Where { model_id: TypeId, - skip_models: std::collections::BTreeMap, + skip_models: std::collections::BTreeMap, aggregates: bool, } @@ -32,7 +32,7 @@ impl Where { fn nested(&self, name: &str, model_id: TypeId) -> Self { let mut skip_models = self.skip_models.clone(); - skip_models.insert(self.model_id, name.to_string()); + skip_models.insert(self.model_id.name().unwrap().unwrap(), name.to_string()); Self { model_id, skip_models, @@ -61,7 +61,7 @@ impl TypeGen for Where { for (key, prop) in model.iter_props() { match prop { Property::Model(prop) => { - let inner = match self.skip_models.get(&prop.model_type.type_id) { + let inner = match self.skip_models.get(&prop.model_type.name()[..]) { Some(name) => t::ref_(name.clone(), Default::default()).build()?, None => context.generate(&self.nested(&name, prop.model_type.type_id))?, }; @@ -121,24 +121,25 @@ impl TypeGen for Where { }; let suffix2 = if !self.skip_models.is_empty() { - let nested_suffix = if self.aggregates { + /* let nested_suffix = if self.aggregates { "_where_with_aggregates" } else { "_where" - }; - - format!( - "_excluding_{}", - self.skip_models - .keys() - .map(|id| id.name().unwrap().unwrap()) - .map(|name| name - .strip_suffix(nested_suffix) - .map(|str| str.to_owned()) - .unwrap_or(name)) - .collect::>() - .join("_and_") - ) + }; */ + + let mut keys = self + .skip_models + .keys() + .cloned() + // .map(|id| id.name().unwrap().unwrap()) + // .map(|name| { + // name.strip_suffix(nested_suffix) + // .map(|str| str.to_owned()) + // .unwrap_or(name) + // }) + .collect::>(); + keys.sort(); + format!("_excluding_{}", keys.join("_and_")) } else { "".to_string() }; diff --git a/src/typegraph/core/src/runtimes/typegate.rs b/src/typegraph/core/src/runtimes/typegate.rs index 61d351882..37c811083 100644 --- a/src/typegraph/core/src/runtimes/typegate.rs +++ b/src/typegraph/core/src/runtimes/typegate.rs @@ -21,6 +21,7 @@ pub enum TypegateOperation { FindPrismaModels, RawPrismaQuery, QueryPrismaModel, + Ping, } impl MaterializerConverter for TypegateOperation { @@ -44,6 +45,7 @@ impl MaterializerConverter for TypegateOperation { Self::FindPrismaModels => "findPrismaModels", Self::RawPrismaQuery => "execRawPrismaQuery", Self::QueryPrismaModel => "queryPrismaModel", + Self::Ping => "ping", } .to_string(), runtime, diff --git a/src/typegraph/core/src/snapshots/typegraph_core__tests__successful_serialization.snap b/src/typegraph/core/src/snapshots/typegraph_core__tests__successful_serialization.snap index 29f6ea430..f8e5b2a0d 100644 --- a/src/typegraph/core/src/snapshots/typegraph_core__tests__successful_serialization.snap +++ b/src/typegraph/core/src/snapshots/typegraph_core__tests__successful_serialization.snap @@ -47,7 +47,7 @@ expression: typegraph.0 }, { "type": "integer", - "title": "root_one_fn_input_two_integer", + "title": "integer_f0e37", "minimum": 12, "maximum": 44 }, diff --git a/src/typegraph/core/src/utils/mod.rs b/src/typegraph/core/src/utils/mod.rs index adf261340..fe424aaeb 100644 --- a/src/typegraph/core/src/utils/mod.rs +++ b/src/typegraph/core/src/utils/mod.rs @@ -114,8 +114,8 @@ impl crate::wit::utils::Guest for crate::Lib { .build(named_provider(&service_name)?) } - fn gql_deploy_query(params: QueryDeployParams) -> Result { - let query = r" + fn gql_deploy_query(params: QueryDeployParams) -> String { + let query = " mutation InsertTypegraph($tg: String!, $secrets: String!, $targetVersion: String!) { addTypegraph(fromString: $tg, secrets: $secrets, targetVersion: $targetVersion) { name @@ -128,8 +128,8 @@ impl crate::wit::utils::Guest for crate::Lib { let mut secrets_map = IndexMap::new(); if let Some(secrets) = params.secrets { - for item in secrets { - secrets_map.insert(item.0, item.1); + for (secret, value) in secrets { + secrets_map.insert(secret, value); } } @@ -143,11 +143,11 @@ impl crate::wit::utils::Guest for crate::Lib { }), }); - Ok(req_body.to_string()) + req_body.to_string() } - fn gql_remove_query(names: Vec) -> Result { - let query = r" + fn gql_remove_query(names: Vec) -> String { + let query = " mutation($names: [String!]!) { removeTypegraphs(names: $names) } @@ -156,9 +156,20 @@ impl crate::wit::utils::Guest for crate::Lib { "query": query, "variables": json!({ "names": names, - }), + }), + }); + + req_body.to_string() + } + + fn gql_ping_query() -> String { + let query = "query { ping }"; + let req_body = json!({ + "query": query, + "variables": json!({}), }); - Ok(req_body.to_string()) + + req_body.to_string() } fn metagen_exec(config: FdkConfig) -> Result> { diff --git a/src/typegraph/core/src/utils/postprocess/naming.rs b/src/typegraph/core/src/utils/postprocess/naming.rs index 1a062eb00..d3d575282 100644 --- a/src/typegraph/core/src/utils/postprocess/naming.rs +++ b/src/typegraph/core/src/utils/postprocess/naming.rs @@ -10,6 +10,8 @@ use common::typegraph::{ visitor::{Edge, PathSegment}, StringFormat, TypeNode, Typegraph, }; +use indexmap::IndexSet; +use sha2::{Digest, Sha256}; use crate::errors::TgError; @@ -25,10 +27,6 @@ impl super::PostProcessor for NamingProcessor { tg, user_named: self.user_named, }; - let mut acc = VisitCollector { - named_types: Default::default(), - path: vec![], - }; let TypeNode::Object { data: root_data, .. @@ -36,6 +34,20 @@ impl super::PostProcessor for NamingProcessor { else { panic!("first item must be root object") }; + + let mut ref_counters = TypeRefCount { + counts: Default::default(), + }; + for (_, &ty_id) in &root_data.properties { + collect_ref_info(&cx, &mut ref_counters, ty_id, &mut HashSet::new())?; + } + + let mut acc = VisitCollector { + named_types: Default::default(), + path: vec![], + // ref related + counts: ref_counters.counts, + }; for (key, &ty_id) in &root_data.properties { acc.path.push(( PathSegment { @@ -62,6 +74,41 @@ struct VisitContext<'a> { struct VisitCollector { named_types: HashMap>, path: Vec<(PathSegment, Rc)>, + // ref related + counts: HashMap>, +} + +struct TypeRefCount { + pub counts: HashMap>, +} + +impl TypeRefCount { + pub fn new_hit(&mut self, id: u32, referrer: u32) { + self.counts + .entry(id) + .and_modify(|counter| { + counter.insert(referrer); + }) + .or_insert(IndexSet::from([referrer])); + } +} + +impl VisitCollector { + pub fn has_more_than_one_referrer(&self, id: u32) -> bool { + if let Some(referrers) = self.counts.get(&id) { + return referrers.len() > 1; + } + + false + } + + pub fn next_name(&mut self, name: String, hash_input: String) -> String { + let mut sha256 = Sha256::new(); + sha256.update(hash_input.bytes().collect::>()); + let hash = format!("{:x}", sha256.finalize()); + + format!("{name}_{}", hash.chars().take(5).collect::()) + } } fn visit_type(cx: &VisitContext, acc: &mut VisitCollector, id: u32) -> anyhow::Result> { @@ -215,14 +262,89 @@ fn visit_type(cx: &VisitContext, acc: &mut VisitCollector, id: u32) -> anyhow::R }; acc.named_types.insert(id, name.clone()); + Ok(name) } +fn collect_ref_info( + cx: &VisitContext, + acc: &mut TypeRefCount, + id: u32, + visited: &mut HashSet, +) -> anyhow::Result<()> { + if cx.user_named.contains(&id) || visited.contains(&id) { + return Ok(()); + } + + visited.insert(id); + + let node = &cx.tg.types[id as usize]; + match node { + TypeNode::Boolean { .. } + | TypeNode::Float { .. } + | TypeNode::Integer { .. } + | TypeNode::String { .. } + | TypeNode::File { .. } + | TypeNode::Any { .. } => { + // base case + } + TypeNode::Optional { data, .. } => { + acc.new_hit(data.item, id); + collect_ref_info(cx, acc, data.item, visited)?; + } + TypeNode::Object { data, .. } => { + for (_, key_id) in &data.properties { + acc.new_hit(*key_id, id); + collect_ref_info(cx, acc, *key_id, visited)?; + } + } + TypeNode::Function { data, .. } => { + acc.new_hit(data.input, id); + acc.new_hit(data.output, id); + + collect_ref_info(cx, acc, data.input, visited)?; + collect_ref_info(cx, acc, data.output, visited)?; + } + TypeNode::List { data, .. } => { + acc.new_hit(data.items, id); + collect_ref_info(cx, acc, data.items, visited)?; + } + TypeNode::Union { data, .. } => { + for variant in &data.any_of { + acc.new_hit(*variant, id); + collect_ref_info(cx, acc, *variant, visited)?; + } + } + TypeNode::Either { data, .. } => { + for variant in &data.one_of { + acc.new_hit(*variant, id); + collect_ref_info(cx, acc, *variant, visited)?; + } + } + }; + + visited.remove(&id); + + Ok(()) +} + fn gen_name(cx: &VisitContext, acc: &mut VisitCollector, id: u32, ty_name: &str) -> Rc { + let node = &cx.tg.types[id as usize]; let name: Rc = if cx.user_named.contains(&id) { - let node = &cx.tg.types[id as usize]; node.base().title.clone().into() } else { + macro_rules! join_if_ok { + ($prefix:expr, $default:expr) => { + if acc.has_more_than_one_referrer(id) { + // Cannot be opinionated on the prefix path if shared (confusing) + let hash_input = $prefix; + acc.next_name(ty_name.to_string(), hash_input) + } else { + format!("{}_{}", $prefix, $default) + } + }; + } + let title; let mut last = acc.path.len(); loop { @@ -232,11 +354,13 @@ fn gen_name(cx: &VisitContext, acc: &mut VisitCollector, id: u32, ty_name: &str) // we don't include optional and list nodes in // generated names (useless but also, they might be placeholders) Edge::OptionalItem | Edge::ArrayItem => continue, - Edge::FunctionInput => format!("{last_name}_input"), - Edge::FunctionOutput => format!("{last_name}_output"), - Edge::ObjectProp(key) => format!("{last_name}_{key}_{ty_name}"), + Edge::FunctionInput => join_if_ok!(last_name.to_string(), "input"), + Edge::FunctionOutput => join_if_ok!(last_name.to_string(), "output"), + Edge::ObjectProp(key) => { + join_if_ok!(format!("{last_name}_{key}"), ty_name) + } Edge::EitherVariant(idx) | Edge::UnionVariant(idx) => { - format!("{last_name}_t{idx}_{ty_name}") + join_if_ok!(format!("{last_name}_t{idx}"), ty_name) } }; break; diff --git a/src/typegraph/core/src/validation/errors.rs b/src/typegraph/core/src/validation/errors.rs index 41c4467f5..63e11f7a2 100644 --- a/src/typegraph/core/src/validation/errors.rs +++ b/src/typegraph/core/src/validation/errors.rs @@ -17,7 +17,3 @@ pub fn invalid_output_type_predefined(name: &str, expected: &str, got: &str) -> ) .into() } - -pub fn unknown_predefined_function(name: &str) -> Error { - format!("unknown predefined function {}", name).into() -} diff --git a/src/typegraph/core/src/validation/materializers.rs b/src/typegraph/core/src/validation/materializers.rs index c05459aeb..1901efcf0 100644 --- a/src/typegraph/core/src/validation/materializers.rs +++ b/src/typegraph/core/src/validation/materializers.rs @@ -1,6 +1,8 @@ // Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. // SPDX-License-Identifier: MPL-2.0 +use common::typegraph::runtimes::deno::PredefinedFunctionMatData; + use crate::runtimes::{DenoMaterializer, MaterializerData, Runtime}; use crate::types::{AsTypeDefEx as _, TypeDef, TypeId}; use crate::wit::core::TypeFunc; @@ -26,43 +28,54 @@ impl Materializer { fn validate_deno_mat(mat_data: &DenoMaterializer, func: &TypeFunc) -> Result<()> { match mat_data { DenoMaterializer::Predefined(predef) => { - match predef.name.as_str() { - "identity" => { + use PredefinedFunctionMatData as P; + match predef { + P::Identity => { if !type_utils::is_equal(func.inp.into(), func.out.into())? { return Err(errors::invalid_output_type_predefined( - &predef.name, + "identity", &TypeId(func.inp).repr()?, &TypeId(func.out).repr()?, )); } } - - "true" | "false" => { + P::True | P::False => { if let Ok(xdef) = TypeId(func.out).as_xdef() { let TypeDef::Boolean(_) = xdef.type_def else { return Err(errors::invalid_output_type_predefined( - &predef.name, + match predef { + P::True => "true", + P::False => "false", + _ => unreachable!(), + }, "bool", &TypeId(func.out).repr()?, )); }; } } - - "allow" | "deny" | "pass" => { + P::Allow + | P::Deny + | P::Pass + | P::ContextCheck { .. } + | P::InternalPolicy { .. } => { if let Ok(xdef) = TypeId(func.out).as_xdef() { let TypeDef::String(_) = xdef.type_def else { return Err(errors::invalid_output_type_predefined( - &predef.name, + match predef { + P::Allow => "allow", + P::Deny => "deny", + P::Pass => "pass", + P::ContextCheck { .. } => "context_check", + P::InternalPolicy { .. } => "internal_policy", + _ => unreachable!(), + }, "string", &TypeId(func.out).repr()?, )); }; } } - _ => { - return Err(errors::unknown_predefined_function(&predef.name)); - } } Ok(()) } diff --git a/src/typegraph/core/wit/typegraph.wit b/src/typegraph/core/wit/typegraph.wit index cda10f336..ed45a6c18 100644 --- a/src/typegraph/core/wit/typegraph.wit +++ b/src/typegraph/core/wit/typegraph.wit @@ -268,6 +268,7 @@ interface runtimes { record materializer-deno-predefined { name: string, + param: option, } record materializer-deno-import { @@ -471,6 +472,7 @@ interface runtimes { raw-prisma-update, raw-prisma-delete, query-prisma-model, + ping, } register-typegate-materializer: func(operation: typegate-operation) -> result; @@ -637,8 +639,9 @@ interface utils { secrets: option>>, } - gql-deploy-query: func(params: query-deploy-params) -> result; - gql-remove-query: func(tg-name: list) -> result; + gql-deploy-query: func(params: query-deploy-params) -> string; + gql-remove-query: func(tg-name: list) -> string; + gql-ping-query: func() -> string; record fdk-config { workspace-path: string, diff --git a/src/typegraph/deno/src/runtimes/deno.ts b/src/typegraph/deno/src/runtimes/deno.ts index 9eb821093..36b2dce64 100644 --- a/src/typegraph/deno/src/runtimes/deno.ts +++ b/src/typegraph/deno/src/runtimes/deno.ts @@ -134,9 +134,9 @@ export class DenoRuntime extends Runtime { fetchContext(outputShape?: C): t.Func { const returnValue = outputShape ? `context` : "JSON.stringify(context)"; return this.func( - t.struct({}), + t.struct({}), outputShape ?? t.json(), - { code: `(_, { context }) => ${returnValue}` } + { code: `(_, { context }) => ${returnValue}` }, ); } diff --git a/src/typegraph/deno/src/tg_deploy.ts b/src/typegraph/deno/src/tg_deploy.ts index 498ef7a61..fe3213268 100644 --- a/src/typegraph/deno/src/tg_deploy.ts +++ b/src/typegraph/deno/src/tg_deploy.ts @@ -85,6 +85,10 @@ export async function tgDeploy( if (typegate.auth) { headers.append("Authorization", typegate.auth.asHeaderValue()); } + const url = new URL("/typegate", typegate.url); + + // Make sure we have the correct credentials before doing anything + await pingTypegate(url, headers); if (refArtifacts.length > 0) { // upload the artifacts @@ -103,7 +107,7 @@ export async function tgDeploy( // deploy the typegraph const response = (await execRequest( - new URL("/typegate", typegate.url), + url, { method: "POST", headers, @@ -165,3 +169,18 @@ export async function tgRemove( return { typegate: response }; } + +export async function pingTypegate( + url: URL, + headers: Headers, +) { + await execRequest( + url, + { + method: "POST", + headers, + body: wit_utils.gqlPingQuery(), + }, + "Failed to access typegate", + ); +} diff --git a/src/typegraph/python/typegraph/graph/tg_deploy.py b/src/typegraph/python/typegraph/graph/tg_deploy.py index af5d01e31..84946de10 100644 --- a/src/typegraph/python/typegraph/graph/tg_deploy.py +++ b/src/typegraph/python/typegraph/graph/tg_deploy.py @@ -8,12 +8,11 @@ from platform import python_version from typegraph.gen.exports.utils import QueryDeployParams -from typegraph.gen.types import Err from typegraph.gen.exports.core import MigrationAction, PrismaMigrationConfig from typegraph.graph.shared_types import BasicAuth from typegraph.graph.tg_artifact_upload import ArtifactUploader from typegraph.graph.typegraph import TypegraphOutput -from typegraph.wit import ErrorStack, SerializeParams, store, wit_utils +from typegraph.wit import SerializeParams, store, wit_utils from typegraph import version as sdk_version from typegraph.io import Log @@ -71,6 +70,9 @@ def tg_deploy(tg: TypegraphOutput, params: TypegraphDeployParams) -> DeployResul if typegate.auth is not None: headers["Authorization"] = typegate.auth.as_header_value() + # Make sure we have the correct credentials before doing anything + ping_typegate(url, headers) + serialize_params = SerializeParams( typegraph_path=params.typegraph_path, prefix=params.prefix, @@ -104,7 +106,7 @@ def tg_deploy(tg: TypegraphOutput, params: TypegraphDeployParams) -> DeployResul artifact_uploader.upload_artifacts() # deploy the typegraph - res = wit_utils.gql_deploy_query( + query = wit_utils.gql_deploy_query( store, params=QueryDeployParams( tg=tg_json, @@ -112,14 +114,11 @@ def tg_deploy(tg: TypegraphOutput, params: TypegraphDeployParams) -> DeployResul ), ) - if isinstance(res, Err): - raise ErrorStack(res.value) - req = request.Request( url=url, method="POST", headers=headers, - data=res.value.encode(), + data=query.encode(), ) response = exec_request(req) @@ -152,16 +151,13 @@ def tg_remove(typegraph_name: str, params: TypegraphRemoveParams): if typegate.auth is not None: headers["Authorization"] = typegate.auth.as_header_value() - res = wit_utils.gql_remove_query(store, [typegraph_name]) - - if isinstance(res, Err): - raise ErrorStack(res.value) + query = wit_utils.gql_remove_query(store, [typegraph_name]) req = request.Request( url=url, method="POST", headers=headers, - data=res.value.encode(), + data=query.encode(), ) response = exec_request(req).read().decode() @@ -186,3 +182,19 @@ def handle_response(res: Any, url=""): return json.loads(res) except Exception as _: raise Exception(f'Expected json object: got "{res}": {url}') + + +def ping_typegate(url: str, headers: dict[str, str]): + req = request.Request( + url=url, + method="POST", + headers=headers, + data=wit_utils.gql_ping_query(store).encode(), + ) + + try: + _ = request.urlopen(req) + except request.HTTPError as e: + raise Exception(f"Failed to access to typegate: {e}") + except Exception as e: + raise Exception(f"{e}: {req.full_url}") diff --git a/src/typegraph/python/typegraph/runtimes/deno.py b/src/typegraph/python/typegraph/runtimes/deno.py index 08dc00512..b223c0e1a 100644 --- a/src/typegraph/python/typegraph/runtimes/deno.py +++ b/src/typegraph/python/typegraph/runtimes/deno.py @@ -118,7 +118,7 @@ def identity(self, inp: "t.struct") -> "t.func": from typegraph import t res = runtimes.get_predefined_deno_func( - store, MaterializerDenoPredefined(name="identity") + store, MaterializerDenoPredefined(name="identity", param=None) ) if isinstance(res, Err): raise Exception(res.value) diff --git a/tests/e2e/published/published_test.ts b/tests/e2e/published/published_test.ts index 2d989713f..93fb9cb03 100644 --- a/tests/e2e/published/published_test.ts +++ b/tests/e2e/published/published_test.ts @@ -44,6 +44,7 @@ const syncConfig = transformSyncConfig({ s3_secret_key: syncEnvs.SYNC_S3_SECRET_KEY, s3_bucket: syncEnvs.SYNC_S3_BUCKET, s3_path_style: true, + force_remove: false }); // put here typegraphs that are to be excluded diff --git a/tests/e2e/self_deploy/self_deploy_bad_cred_test.ts b/tests/e2e/self_deploy/self_deploy_bad_cred_test.ts new file mode 100644 index 000000000..7d50d40fd --- /dev/null +++ b/tests/e2e/self_deploy/self_deploy_bad_cred_test.ts @@ -0,0 +1,40 @@ +// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +// SPDX-License-Identifier: MPL-2.0 +import { BasicAuth, tgDeploy } from "@typegraph/sdk/tg_deploy"; + +import { Meta } from "test-utils/mod.ts"; +import { tg } from "./self_deploy.ts"; +import { testDir } from "test-utils/dir.ts"; +import { join } from "@std/path/join"; +import { unreachable } from "@std/assert"; +import * as path from "@std/path"; +import { assertStringIncludes } from "@std/assert/string-includes"; + +Meta.test( + { + name: "typegate should fail after ping on bad credential", + }, + async (t) => { + const gate = `http://localhost:${t.port}`; + const auth = new BasicAuth("admin", "wrong password"); + const cwdDir = join(testDir, "e2e", "self_deploy"); + + try { + const _ = await tgDeploy(tg, { + typegate: { url: gate, auth }, + secrets: {}, + typegraphPath: path.join(cwdDir, "self_deploy.ts"), + migrationsDir: `${cwdDir}/prisma-migrations`, + defaultMigrationAction: { + apply: true, + create: true, + reset: false, + }, + }); + + unreachable(); + } catch(err) { + assertStringIncludes(JSON.stringify(err instanceof Error ? err.message : err), "Failed to access typegate: request failed with status 401 (Unauthorized)"); + } + }, +); diff --git a/tests/e2e/self_deploy/self_deploy_test.ts b/tests/e2e/self_deploy/self_deploy_test.ts index 07f865812..a16a8b7ea 100644 --- a/tests/e2e/self_deploy/self_deploy_test.ts +++ b/tests/e2e/self_deploy/self_deploy_test.ts @@ -21,7 +21,7 @@ Meta.test( const { serialized, response: gateResponseAdd } = await tgDeploy(tg, { typegate: { url: gate, auth }, secrets: {}, - typegraphPath: path.join(cwdDir, "self_deploy.mjs"), + typegraphPath: path.join(cwdDir, "self_deploy.ts"), migrationsDir: `${cwdDir}/prisma-migrations`, defaultMigrationAction: { apply: true, diff --git a/tests/e2e/typegraph/__snapshots__/typegraph_test.ts.snap b/tests/e2e/typegraph/__snapshots__/typegraph_test.ts.snap index 4f0039f62..782d9a47d 100644 --- a/tests/e2e/typegraph/__snapshots__/typegraph_test.ts.snap +++ b/tests/e2e/typegraph/__snapshots__/typegraph_test.ts.snap @@ -302,7 +302,7 @@ snapshot[`typegraphs creation 2`] = ` }, { "type": "object", - "title": "root_add_fn_input", + "title": "struct_aada4", "properties": { "first": 3, "second": 3 @@ -316,7 +316,7 @@ snapshot[`typegraphs creation 2`] = ` }, { "type": "float", - "title": "root_add_fn_input_first_float" + "title": "float_9d392" }, { "type": "function", @@ -454,7 +454,7 @@ snapshot[`typegraphs creation 3`] = ` }, { "type": "object", - "title": "root_one_fn_input", + "title": "struct_62ec9", "properties": { "a": 3, "b": 4 @@ -468,11 +468,11 @@ snapshot[`typegraphs creation 3`] = ` }, { "type": "integer", - "title": "root_one_fn_input_a_integer" + "title": "struct_62ec9_a_integer" }, { "type": "integer", - "title": "root_one_fn_input_b_integer", + "title": "struct_62ec9_b_integer", "minimum": 12 }, { @@ -971,7 +971,7 @@ snapshot[`typegraphs creation 5`] = ` }, { "type": "object", - "title": "root_add_fn_input", + "title": "struct_aada4", "properties": { "first": 3, "second": 3 @@ -985,7 +985,7 @@ snapshot[`typegraphs creation 5`] = ` }, { "type": "float", - "title": "root_add_fn_input_first_float" + "title": "float_9d392" }, { "type": "function", @@ -1123,7 +1123,7 @@ snapshot[`typegraphs creation 6`] = ` }, { "type": "object", - "title": "root_one_fn_input", + "title": "struct_62ec9", "properties": { "a": 3, "b": 4 @@ -1137,11 +1137,11 @@ snapshot[`typegraphs creation 6`] = ` }, { "type": "integer", - "title": "root_one_fn_input_a_integer" + "title": "struct_62ec9_a_integer" }, { "type": "integer", - "title": "root_one_fn_input_b_integer", + "title": "struct_62ec9_b_integer", "minimum": 12 }, { diff --git a/tests/metagen/__snapshots__/metagen_test.ts.snap b/tests/metagen/__snapshots__/metagen_test.ts.snap index 76e6d03fa..99f9dd69a 100644 --- a/tests/metagen/__snapshots__/metagen_test.ts.snap +++ b/tests/metagen/__snapshots__/metagen_test.ts.snap @@ -91,11 +91,11 @@ class Struct: @dataclass -class RootOneFnInput(Struct): +class Struct62ec9(Struct): name: str -FORWARD_REFS["RootOneFnInput"] = RootOneFnInput +FORWARD_REFS["Struct62ec9"] = Struct62ec9 @dataclass @@ -116,9 +116,9 @@ def __repr(value: Any): -def typed_three(user_fn: Callable[[RootOneFnInput, Ctx], Student]): +def typed_three(user_fn: Callable[[Struct62ec9, Ctx], Student]): def exported_wrapper(raw_inp, ctx): - inp: RootOneFnInput = Struct.new(RootOneFnInput, raw_inp) + inp: Struct62ec9 = Struct.new(Struct62ec9, raw_inp) out: Student = user_fn(inp, ctx) if isinstance(out, list): return [__repr(v) for v in out] @@ -136,11 +136,11 @@ def typed_three(user_fn: Callable[[RootOneFnInput, Ctx], Student]): # are supported. I.e. prefixed by \`.\` or \`..\` # - Make sure to include any module imports in the \`deps\` # array when using external modules with PythonRuntime -from .other_types import RootOneFnInput, Student, typed_three +from .other_types import Struct62ec9, Student, typed_three @typed_three -def three(inp: RootOneFnInput, ctx: Ctx) -> Student: +def three(inp: Struct62ec9, ctx: Ctx) -> Student: # TODO: write your logic here raise Exception("three not implemented") @@ -237,11 +237,11 @@ class Struct: @dataclass -class RootOneFnInput(Struct): +class Struct62ec9(Struct): name: str -FORWARD_REFS["RootOneFnInput"] = RootOneFnInput +FORWARD_REFS["Struct62ec9"] = Struct62ec9 @dataclass @@ -263,7 +263,7 @@ FORWARD_REFS["TwoInput"] = TwoInput TypeRootOneFnOutput = List["Student"] -TypeRootOneFnInputNameString = str +TypeString5fd9b = str def __repr(value: Any): if isinstance(value, Struct): @@ -272,9 +272,9 @@ def __repr(value: Any): -def typed_fnOne(user_fn: Callable[[RootOneFnInput, Ctx], TypeRootOneFnOutput]): +def typed_fnOne(user_fn: Callable[[Struct62ec9, Ctx], TypeRootOneFnOutput]): def exported_wrapper(raw_inp, ctx): - inp: RootOneFnInput = Struct.new(RootOneFnInput, raw_inp) + inp: Struct62ec9 = Struct.new(Struct62ec9, raw_inp) out: TypeRootOneFnOutput = user_fn(inp, ctx) if isinstance(out, list): return [__repr(v) for v in out] @@ -283,10 +283,10 @@ def typed_fnOne(user_fn: Callable[[RootOneFnInput, Ctx], TypeRootOneFnOutput]): return exported_wrapper -def typed_fnTwo(user_fn: Callable[[TwoInput, Ctx], TypeRootOneFnInputNameString]): +def typed_fnTwo(user_fn: Callable[[TwoInput, Ctx], TypeString5fd9b]): def exported_wrapper(raw_inp, ctx): inp: TwoInput = Struct.new(TwoInput, raw_inp) - out: TypeRootOneFnInputNameString = user_fn(inp, ctx) + out: TypeString5fd9b = user_fn(inp, ctx) if isinstance(out, list): return [__repr(v) for v in out] return __repr(out) @@ -303,16 +303,16 @@ def typed_fnTwo(user_fn: Callable[[TwoInput, Ctx], TypeRootOneFnInputNameString] # are supported. I.e. prefixed by \`.\` or \`..\` # - Make sure to include any module imports in the \`deps\` # array when using external modules with PythonRuntime -from .same_hit_types import RootOneFnInput, TwoInput, TypeRootOneFnInputNameString, TypeRootOneFnOutput, typed_fnOne, typed_fnTwo +from .same_hit_types import Struct62ec9, TwoInput, TypeRootOneFnOutput, TypeString5fd9b, typed_fnOne, typed_fnTwo @typed_fnOne -def fnOne(inp: RootOneFnInput, ctx: Ctx) -> TypeRootOneFnOutput: +def fnOne(inp: Struct62ec9, ctx: Ctx) -> TypeRootOneFnOutput: # TODO: write your logic here raise Exception("fnOne not implemented") @typed_fnTwo -def fnTwo(inp: TwoInput, ctx: Ctx) -> TypeRootOneFnInputNameString: +def fnTwo(inp: TwoInput, ctx: Ctx) -> TypeString5fd9b: # TODO: write your logic here raise Exception("fnTwo not implemented") @@ -564,23 +564,22 @@ macro_rules! init_mat { // gen-static-end use types::*; pub mod types { - pub type RootOneFnInputNameString = String; #[derive(Debug, serde::Serialize, serde::Deserialize)] - pub struct RootOneFnInput { - pub name: RootOneFnInputNameString, + pub struct Struct62ec9 { + pub name: String, } pub type StudentIdInteger = i64; pub type StudentPeersPlaceholderOptional = Option; #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct Student { pub id: StudentIdInteger, - pub name: RootOneFnInputNameString, + pub name: String, pub peers: StudentPeersPlaceholderOptional, } pub type RootOneFnOutput = Vec; #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct TwoInput { - pub name: RootOneFnInputNameString, + pub name: String, } } pub mod stubs { @@ -603,7 +602,7 @@ pub mod stubs { } } - fn handle(&self, input: RootOneFnInput, cx: Ctx) -> anyhow::Result; + fn handle(&self, input: Struct62ec9, cx: Ctx) -> anyhow::Result; } pub trait RootTwoFn: Sized + 'static { fn erased(self) -> ErasedHandler { @@ -623,7 +622,7 @@ pub mod stubs { } } - fn handle(&self, input: TwoInput, cx: Ctx) -> anyhow::Result; + fn handle(&self, input: TwoInput, cx: Ctx) -> anyhow::Result; } pub trait RootThreeFn: Sized + 'static { fn erased(self) -> ErasedHandler { @@ -643,7 +642,7 @@ pub mod stubs { } } - fn handle(&self, input: RootOneFnInput, cx: Ctx) -> anyhow::Result; + fn handle(&self, input: Struct62ec9, cx: Ctx) -> anyhow::Result; } pub fn op_to_trait_name(op_name: &str) -> &'static str { match op_name { @@ -719,26 +718,25 @@ export type Handler = ( tg: Deployment, ) => Out | Promise; -export type RootOneFnInputNameString = string; -export type RootOneFnInput = { - name: RootOneFnInputNameString; +export type Struct62ec9 = { + name: string; }; export type StudentIdInteger = number; export type StudentPeersPlaceholderOptional = RootOneFnOutput | null | undefined; export type Student = { id: StudentIdInteger; - name: RootOneFnInputNameString; + name: string; peers?: StudentPeersPlaceholderOptional; }; export type RootOneFnOutput = Array; export type TwoInput = { - name: RootOneFnInputNameString; + name: string; }; -export type RootOneFnHandler = Handler; -export type RootTwoFnHandler = Handler; -export type RootThreeFnHandler = Handler; +export type RootOneFnHandler = Handler; +export type RootTwoFnHandler = Handler; +export type RootThreeFnHandler = Handler; ', overwrite: true, path: "./workspace/some/base/path/ts/fdk.ts", @@ -892,11 +890,11 @@ class Struct: @dataclass -class RootOneFnInput(Struct): +class Struct62ec9(Struct): name: str -FORWARD_REFS["RootOneFnInput"] = RootOneFnInput +FORWARD_REFS["Struct62ec9"] = Struct62ec9 @dataclass @@ -917,9 +915,9 @@ def __repr(value: Any): -def typed_three(user_fn: Callable[[RootOneFnInput, Ctx], Student]): +def typed_three(user_fn: Callable[[Struct62ec9, Ctx], Student]): def exported_wrapper(raw_inp, ctx): - inp: RootOneFnInput = Struct.new(RootOneFnInput, raw_inp) + inp: Struct62ec9 = Struct.new(Struct62ec9, raw_inp) out: Student = user_fn(inp, ctx) if isinstance(out, list): return [__repr(v) for v in out] @@ -937,11 +935,11 @@ def typed_three(user_fn: Callable[[RootOneFnInput, Ctx], Student]): # are supported. I.e. prefixed by \`.\` or \`..\` # - Make sure to include any module imports in the \`deps\` # array when using external modules with PythonRuntime -from .other_types import RootOneFnInput, Student, typed_three +from .other_types import Struct62ec9, Student, typed_three @typed_three -def three(inp: RootOneFnInput, ctx: Ctx) -> Student: +def three(inp: Struct62ec9, ctx: Ctx) -> Student: # TODO: write your logic here raise Exception("three not implemented") @@ -1038,11 +1036,11 @@ class Struct: @dataclass -class RootOneFnInput(Struct): +class Struct62ec9(Struct): name: str -FORWARD_REFS["RootOneFnInput"] = RootOneFnInput +FORWARD_REFS["Struct62ec9"] = Struct62ec9 @dataclass @@ -1064,7 +1062,7 @@ FORWARD_REFS["TwoInput"] = TwoInput TypeRootOneFnOutput = List["Student"] -TypeRootOneFnInputNameString = str +TypeString5fd9b = str def __repr(value: Any): if isinstance(value, Struct): @@ -1073,9 +1071,9 @@ def __repr(value: Any): -def typed_fnOne(user_fn: Callable[[RootOneFnInput, Ctx], TypeRootOneFnOutput]): +def typed_fnOne(user_fn: Callable[[Struct62ec9, Ctx], TypeRootOneFnOutput]): def exported_wrapper(raw_inp, ctx): - inp: RootOneFnInput = Struct.new(RootOneFnInput, raw_inp) + inp: Struct62ec9 = Struct.new(Struct62ec9, raw_inp) out: TypeRootOneFnOutput = user_fn(inp, ctx) if isinstance(out, list): return [__repr(v) for v in out] @@ -1084,10 +1082,10 @@ def typed_fnOne(user_fn: Callable[[RootOneFnInput, Ctx], TypeRootOneFnOutput]): return exported_wrapper -def typed_fnTwo(user_fn: Callable[[TwoInput, Ctx], TypeRootOneFnInputNameString]): +def typed_fnTwo(user_fn: Callable[[TwoInput, Ctx], TypeString5fd9b]): def exported_wrapper(raw_inp, ctx): inp: TwoInput = Struct.new(TwoInput, raw_inp) - out: TypeRootOneFnInputNameString = user_fn(inp, ctx) + out: TypeString5fd9b = user_fn(inp, ctx) if isinstance(out, list): return [__repr(v) for v in out] return __repr(out) @@ -1104,16 +1102,16 @@ def typed_fnTwo(user_fn: Callable[[TwoInput, Ctx], TypeRootOneFnInputNameString] # are supported. I.e. prefixed by \`.\` or \`..\` # - Make sure to include any module imports in the \`deps\` # array when using external modules with PythonRuntime -from .same_hit_types import RootOneFnInput, TwoInput, TypeRootOneFnInputNameString, TypeRootOneFnOutput, typed_fnOne, typed_fnTwo +from .same_hit_types import Struct62ec9, TwoInput, TypeRootOneFnOutput, TypeString5fd9b, typed_fnOne, typed_fnTwo @typed_fnOne -def fnOne(inp: RootOneFnInput, ctx: Ctx) -> TypeRootOneFnOutput: +def fnOne(inp: Struct62ec9, ctx: Ctx) -> TypeRootOneFnOutput: # TODO: write your logic here raise Exception("fnOne not implemented") @typed_fnTwo -def fnTwo(inp: TwoInput, ctx: Ctx) -> TypeRootOneFnInputNameString: +def fnTwo(inp: TwoInput, ctx: Ctx) -> TypeString5fd9b: # TODO: write your logic here raise Exception("fnTwo not implemented") @@ -1365,23 +1363,22 @@ macro_rules! init_mat { // gen-static-end use types::*; pub mod types { - pub type RootOneFnInputNameString = String; #[derive(Debug, serde::Serialize, serde::Deserialize)] - pub struct RootOneFnInput { - pub name: RootOneFnInputNameString, + pub struct Struct62ec9 { + pub name: String, } pub type StudentIdInteger = i64; pub type StudentPeersPlaceholderOptional = Option; #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct Student { pub id: StudentIdInteger, - pub name: RootOneFnInputNameString, + pub name: String, pub peers: StudentPeersPlaceholderOptional, } pub type RootOneFnOutput = Vec; #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct TwoInput { - pub name: RootOneFnInputNameString, + pub name: String, } } pub mod stubs { @@ -1404,7 +1401,7 @@ pub mod stubs { } } - fn handle(&self, input: RootOneFnInput, cx: Ctx) -> anyhow::Result; + fn handle(&self, input: Struct62ec9, cx: Ctx) -> anyhow::Result; } pub trait RootTwoFn: Sized + 'static { fn erased(self) -> ErasedHandler { @@ -1424,7 +1421,7 @@ pub mod stubs { } } - fn handle(&self, input: TwoInput, cx: Ctx) -> anyhow::Result; + fn handle(&self, input: TwoInput, cx: Ctx) -> anyhow::Result; } pub trait RootThreeFn: Sized + 'static { fn erased(self) -> ErasedHandler { @@ -1444,7 +1441,7 @@ pub mod stubs { } } - fn handle(&self, input: RootOneFnInput, cx: Ctx) -> anyhow::Result; + fn handle(&self, input: Struct62ec9, cx: Ctx) -> anyhow::Result; } pub fn op_to_trait_name(op_name: &str) -> &'static str { match op_name { @@ -1520,26 +1517,25 @@ export type Handler = ( tg: Deployment, ) => Out | Promise; -export type RootOneFnInputNameString = string; -export type RootOneFnInput = { - name: RootOneFnInputNameString; +export type Struct62ec9 = { + name: string; }; export type StudentIdInteger = number; export type StudentPeersPlaceholderOptional = RootOneFnOutput | null | undefined; export type Student = { id: StudentIdInteger; - name: RootOneFnInputNameString; + name: string; peers?: StudentPeersPlaceholderOptional; }; export type RootOneFnOutput = Array; export type TwoInput = { - name: RootOneFnInputNameString; + name: string; }; -export type RootOneFnHandler = Handler; -export type RootTwoFnHandler = Handler; -export type RootThreeFnHandler = Handler; +export type RootOneFnHandler = Handler; +export type RootTwoFnHandler = Handler; +export type RootThreeFnHandler = Handler; ', overwrite: true, path: "./workspace/some/base/path/ts/fdk.ts", @@ -1693,11 +1689,11 @@ class Struct: @dataclass -class MyNamespaceRootOneFnInput(Struct): +class MyNamespaceStruct62ec9(Struct): name: str -FORWARD_REFS["RootOneFnInput"] = RootOneFnInput +FORWARD_REFS["Struct62ec9"] = Struct62ec9 @dataclass @@ -1718,9 +1714,9 @@ def __repr(value: Any): -def typed_three(user_fn: Callable[[RootOneFnInput, Ctx], Student]): +def typed_three(user_fn: Callable[[Struct62ec9, Ctx], Student]): def exported_wrapper(raw_inp, ctx): - inp: RootOneFnInput = Struct.new(RootOneFnInput, raw_inp) + inp: Struct62ec9 = Struct.new(Struct62ec9, raw_inp) out: Student = user_fn(inp, ctx) if isinstance(out, list): return [__repr(v) for v in out] @@ -1738,11 +1734,11 @@ def typed_three(user_fn: Callable[[RootOneFnInput, Ctx], Student]): # are supported. I.e. prefixed by \`.\` or \`..\` # - Make sure to include any module imports in the \`deps\` # array when using external modules with PythonRuntime -from .other_types import RootOneFnInput, Student, typed_three +from .other_types import Struct62ec9, Student, typed_three @typed_three -def three(inp: RootOneFnInput, ctx: Ctx) -> Student: +def three(inp: Struct62ec9, ctx: Ctx) -> Student: # TODO: write your logic here raise Exception("three not implemented") @@ -1839,11 +1835,11 @@ class Struct: @dataclass -class MyNamespaceRootOneFnInput(Struct): +class MyNamespaceStruct62ec9(Struct): name: str -FORWARD_REFS["RootOneFnInput"] = RootOneFnInput +FORWARD_REFS["Struct62ec9"] = Struct62ec9 @dataclass @@ -1865,7 +1861,7 @@ FORWARD_REFS["TwoInput"] = TwoInput TypeRootOneFnOutput = List["Student"] -TypeRootOneFnInputNameString = str +TypeString5fd9b = str def __repr(value: Any): if isinstance(value, Struct): @@ -1874,9 +1870,9 @@ def __repr(value: Any): -def typed_fnOne(user_fn: Callable[[RootOneFnInput, Ctx], TypeRootOneFnOutput]): +def typed_fnOne(user_fn: Callable[[Struct62ec9, Ctx], TypeRootOneFnOutput]): def exported_wrapper(raw_inp, ctx): - inp: RootOneFnInput = Struct.new(RootOneFnInput, raw_inp) + inp: Struct62ec9 = Struct.new(Struct62ec9, raw_inp) out: TypeRootOneFnOutput = user_fn(inp, ctx) if isinstance(out, list): return [__repr(v) for v in out] @@ -1885,10 +1881,10 @@ def typed_fnOne(user_fn: Callable[[RootOneFnInput, Ctx], TypeRootOneFnOutput]): return exported_wrapper -def typed_fnTwo(user_fn: Callable[[TwoInput, Ctx], TypeRootOneFnInputNameString]): +def typed_fnTwo(user_fn: Callable[[TwoInput, Ctx], TypeString5fd9b]): def exported_wrapper(raw_inp, ctx): inp: TwoInput = Struct.new(TwoInput, raw_inp) - out: TypeRootOneFnInputNameString = user_fn(inp, ctx) + out: TypeString5fd9b = user_fn(inp, ctx) if isinstance(out, list): return [__repr(v) for v in out] return __repr(out) @@ -1905,16 +1901,16 @@ def typed_fnTwo(user_fn: Callable[[TwoInput, Ctx], TypeRootOneFnInputNameString] # are supported. I.e. prefixed by \`.\` or \`..\` # - Make sure to include any module imports in the \`deps\` # array when using external modules with PythonRuntime -from .same_hit_types import RootOneFnInput, TwoInput, TypeRootOneFnInputNameString, TypeRootOneFnOutput, typed_fnOne, typed_fnTwo +from .same_hit_types import Struct62ec9, TwoInput, TypeRootOneFnOutput, TypeString5fd9b, typed_fnOne, typed_fnTwo @typed_fnOne -def fnOne(inp: RootOneFnInput, ctx: Ctx) -> TypeRootOneFnOutput: +def fnOne(inp: Struct62ec9, ctx: Ctx) -> TypeRootOneFnOutput: # TODO: write your logic here raise Exception("fnOne not implemented") @typed_fnTwo -def fnTwo(inp: TwoInput, ctx: Ctx) -> TypeRootOneFnInputNameString: +def fnTwo(inp: TwoInput, ctx: Ctx) -> TypeString5fd9b: # TODO: write your logic here raise Exception("fnTwo not implemented") @@ -2016,11 +2012,11 @@ class Struct: @dataclass -class MyNamespaceRootOneFnInput(Struct): +class MyNamespaceStruct62ec9(Struct): name: str -FORWARD_REFS["RootOneFnInput"] = RootOneFnInput +FORWARD_REFS["Struct62ec9"] = Struct62ec9 @dataclass @@ -2041,9 +2037,9 @@ def __repr(value: Any): -def typed_three(user_fn: Callable[[RootOneFnInput, Ctx], Student]): +def typed_three(user_fn: Callable[[Struct62ec9, Ctx], Student]): def exported_wrapper(raw_inp, ctx): - inp: RootOneFnInput = Struct.new(RootOneFnInput, raw_inp) + inp: Struct62ec9 = Struct.new(Struct62ec9, raw_inp) out: Student = user_fn(inp, ctx) if isinstance(out, list): return [__repr(v) for v in out] @@ -2061,11 +2057,11 @@ def typed_three(user_fn: Callable[[RootOneFnInput, Ctx], Student]): # are supported. I.e. prefixed by \`.\` or \`..\` # - Make sure to include any module imports in the \`deps\` # array when using external modules with PythonRuntime -from .other_types import RootOneFnInput, Student, typed_three +from .other_types import Struct62ec9, Student, typed_three @typed_three -def three(inp: RootOneFnInput, ctx: Ctx) -> Student: +def three(inp: Struct62ec9, ctx: Ctx) -> Student: # TODO: write your logic here raise Exception("three not implemented") @@ -2162,11 +2158,11 @@ class Struct: @dataclass -class MyNamespaceRootOneFnInput(Struct): +class MyNamespaceStruct62ec9(Struct): name: str -FORWARD_REFS["RootOneFnInput"] = RootOneFnInput +FORWARD_REFS["Struct62ec9"] = Struct62ec9 @dataclass @@ -2188,7 +2184,7 @@ FORWARD_REFS["TwoInput"] = TwoInput TypeRootOneFnOutput = List["Student"] -TypeRootOneFnInputNameString = str +TypeString5fd9b = str def __repr(value: Any): if isinstance(value, Struct): @@ -2197,9 +2193,9 @@ def __repr(value: Any): -def typed_fnOne(user_fn: Callable[[RootOneFnInput, Ctx], TypeRootOneFnOutput]): +def typed_fnOne(user_fn: Callable[[Struct62ec9, Ctx], TypeRootOneFnOutput]): def exported_wrapper(raw_inp, ctx): - inp: RootOneFnInput = Struct.new(RootOneFnInput, raw_inp) + inp: Struct62ec9 = Struct.new(Struct62ec9, raw_inp) out: TypeRootOneFnOutput = user_fn(inp, ctx) if isinstance(out, list): return [__repr(v) for v in out] @@ -2208,10 +2204,10 @@ def typed_fnOne(user_fn: Callable[[RootOneFnInput, Ctx], TypeRootOneFnOutput]): return exported_wrapper -def typed_fnTwo(user_fn: Callable[[TwoInput, Ctx], TypeRootOneFnInputNameString]): +def typed_fnTwo(user_fn: Callable[[TwoInput, Ctx], TypeString5fd9b]): def exported_wrapper(raw_inp, ctx): inp: TwoInput = Struct.new(TwoInput, raw_inp) - out: TypeRootOneFnInputNameString = user_fn(inp, ctx) + out: TypeString5fd9b = user_fn(inp, ctx) if isinstance(out, list): return [__repr(v) for v in out] return __repr(out) @@ -2228,16 +2224,16 @@ def typed_fnTwo(user_fn: Callable[[TwoInput, Ctx], TypeRootOneFnInputNameString] # are supported. I.e. prefixed by \`.\` or \`..\` # - Make sure to include any module imports in the \`deps\` # array when using external modules with PythonRuntime -from .same_hit_types import RootOneFnInput, TwoInput, TypeRootOneFnInputNameString, TypeRootOneFnOutput, typed_fnOne, typed_fnTwo +from .same_hit_types import Struct62ec9, TwoInput, TypeRootOneFnOutput, TypeString5fd9b, typed_fnOne, typed_fnTwo @typed_fnOne -def fnOne(inp: RootOneFnInput, ctx: Ctx) -> TypeRootOneFnOutput: +def fnOne(inp: Struct62ec9, ctx: Ctx) -> TypeRootOneFnOutput: # TODO: write your logic here raise Exception("fnOne not implemented") @typed_fnTwo -def fnTwo(inp: TwoInput, ctx: Ctx) -> TypeRootOneFnInputNameString: +def fnTwo(inp: TwoInput, ctx: Ctx) -> TypeString5fd9b: # TODO: write your logic here raise Exception("fnTwo not implemented") diff --git a/tests/metagen/typegraphs/sample/py/client.py b/tests/metagen/typegraphs/sample/py/client.py index d19492338..f3cd8d388 100644 --- a/tests/metagen/typegraphs/sample/py/client.py +++ b/tests/metagen/typegraphs/sample/py/client.py @@ -873,8 +873,8 @@ def RootScalarArgsFn(): variants=return_node.variants, arg_types={ "id": "UserIdStringUuid", - "slug": "PostSlugString", - "title": "PostSlugString", + "slug": "StringE1a43", + "title": "StringE1a43", }, ) @@ -893,7 +893,7 @@ def RootCompositeArgsFn(): sub_nodes=return_node.sub_nodes, variants=return_node.variants, arg_types={ - "id": "PostSlugString", + "id": "StringE1a43", }, ) @@ -904,7 +904,7 @@ def RootScalarUnionFn(): sub_nodes=return_node.sub_nodes, variants=return_node.variants, arg_types={ - "id": "PostSlugString", + "id": "StringE1a43", }, ) @@ -924,7 +924,7 @@ def RootCompositeUnionFn(): sub_nodes=return_node.sub_nodes, variants=return_node.variants, arg_types={ - "id": "PostSlugString", + "id": "StringE1a43", }, ) @@ -944,7 +944,7 @@ def RootMixedUnionFn(): sub_nodes=return_node.sub_nodes, variants=return_node.variants, arg_types={ - "id": "PostSlugString", + "id": "StringE1a43", }, ) @@ -994,22 +994,20 @@ def RootNestedCompositeFn(): UserIdStringUuid = str -PostSlugString = str - Post = typing.TypedDict( "Post", { "id": UserIdStringUuid, - "slug": PostSlugString, - "title": PostSlugString, + "slug": str, + "title": str, }, total=False, ) -RootCompositeArgsFnInput = typing.TypedDict( - "RootCompositeArgsFnInput", +StructC339c = typing.TypedDict( + "StructC339c", { - "id": PostSlugString, + "id": str, }, total=False, ) @@ -1028,11 +1026,9 @@ def RootNestedCompositeFn(): total=False, ) -RootScalarUnionFnOutputT1Integer = int - RootScalarUnionFnOutput = typing.Union[ - PostSlugString, - RootScalarUnionFnOutputT1Integer, + str, + int, ] @@ -1045,15 +1041,15 @@ def RootNestedCompositeFn(): RootMixedUnionFnOutput = typing.Union[ Post, User, - PostSlugString, - RootScalarUnionFnOutputT1Integer, + str, + int, ] RootNestedCompositeFnOutputCompositeStructNestedStruct = typing.TypedDict( "RootNestedCompositeFnOutputCompositeStructNestedStruct", { - "inner": RootScalarUnionFnOutputT1Integer, + "inner": int, }, total=False, ) @@ -1061,7 +1057,7 @@ def RootNestedCompositeFn(): RootNestedCompositeFnOutputCompositeStruct = typing.TypedDict( "RootNestedCompositeFnOutputCompositeStruct", { - "value": RootScalarUnionFnOutputT1Integer, + "value": int, "nested": RootNestedCompositeFnOutputCompositeStructNestedStruct, }, total=False, @@ -1070,7 +1066,7 @@ def RootNestedCompositeFn(): RootNestedCompositeFnOutputListStruct = typing.TypedDict( "RootNestedCompositeFnOutputListStruct", { - "value": RootScalarUnionFnOutputT1Integer, + "value": int, }, total=False, ) @@ -1082,7 +1078,7 @@ def RootNestedCompositeFn(): RootNestedCompositeFnOutput = typing.TypedDict( "RootNestedCompositeFnOutput", { - "scalar": RootScalarUnionFnOutputT1Integer, + "scalar": int, "composite": RootNestedCompositeFnOutputCompositeStruct, "list": RootNestedCompositeFnOutputListRootNestedCompositeFnOutputListStructList, }, @@ -1183,7 +1179,7 @@ def __init__(self): super().__init__( { "UserIdStringUuid": "String!", - "PostSlugString": "String!", + "StringE1a43": "String!", "post": "post!", "user": "user!", } @@ -1205,7 +1201,7 @@ def get_posts(self, select: PostSelections) -> QueryNode[Post]: node.node_name, node.instance_name, node.args, node.sub_nodes, node.files ) - def scalar_no_args(self) -> QueryNode[PostSlugString]: + def scalar_no_args(self) -> QueryNode[str]: node = selection_to_nodes( {"scalarNoArgs": True}, {"scalarNoArgs": NodeDescs.RootScalarNoArgsFn}, "$q" )[0] @@ -1215,7 +1211,7 @@ def scalar_no_args(self) -> QueryNode[PostSlugString]: def scalar_args( self, args: typing.Union[Post, PlaceholderArgs] - ) -> MutationNode[PostSlugString]: + ) -> MutationNode[str]: node = selection_to_nodes( {"scalarArgs": args}, {"scalarArgs": NodeDescs.RootScalarArgsFn}, "$q" )[0] @@ -1234,9 +1230,7 @@ def composite_no_args(self, select: PostSelections) -> MutationNode[Post]: ) def composite_args( - self, - args: typing.Union[RootCompositeArgsFnInput, PlaceholderArgs], - select: PostSelections, + self, args: typing.Union[StructC339c, PlaceholderArgs], select: PostSelections ) -> MutationNode[Post]: node = selection_to_nodes( {"compositeArgs": (args, select)}, @@ -1248,7 +1242,7 @@ def composite_args( ) def scalar_union( - self, args: typing.Union[RootCompositeArgsFnInput, PlaceholderArgs] + self, args: typing.Union[StructC339c, PlaceholderArgs] ) -> QueryNode[RootScalarUnionFnOutput]: node = selection_to_nodes( {"scalarUnion": args}, {"scalarUnion": NodeDescs.RootScalarUnionFn}, "$q" @@ -1259,7 +1253,7 @@ def scalar_union( def composite_union( self, - args: typing.Union[RootCompositeArgsFnInput, PlaceholderArgs], + args: typing.Union[StructC339c, PlaceholderArgs], select: RootCompositeUnionFnOutputSelections, ) -> QueryNode[RootCompositeUnionFnOutput]: node = selection_to_nodes( @@ -1273,7 +1267,7 @@ def composite_union( def mixed_union( self, - args: typing.Union[RootCompositeArgsFnInput, PlaceholderArgs], + args: typing.Union[StructC339c, PlaceholderArgs], select: RootMixedUnionFnOutputSelections, ) -> QueryNode[RootMixedUnionFnOutput]: node = selection_to_nodes( diff --git a/tests/metagen/typegraphs/sample/py_upload/client.py b/tests/metagen/typegraphs/sample/py_upload/client.py index 393857a97..e7561d279 100644 --- a/tests/metagen/typegraphs/sample/py_upload/client.py +++ b/tests/metagen/typegraphs/sample/py_upload/client.py @@ -828,8 +828,8 @@ def RootUploadFn(): sub_nodes=return_node.sub_nodes, variants=return_node.variants, arg_types={ - "file": "RootUploadFnInputFileFile", - "path": "RootUploadFnInputPathRootUploadFnInputPathStringOptional", + "file": "FileBf9b7", + "path": "RootUploadFnInputPathString25e51Optional", }, input_files=[[".file"]], ) @@ -841,64 +841,54 @@ def RootUploadManyFn(): sub_nodes=return_node.sub_nodes, variants=return_node.variants, arg_types={ - "prefix": "RootUploadManyFnInputPrefixRootUploadFnInputPathStringOptional", - "files": "RootUploadManyFnInputFilesRootUploadFnInputFileFileList", + "prefix": "RootUploadManyFnInputPrefixString25e51Optional", + "files": "RootUploadManyFnInputFilesFileBf9b7List", }, input_files=[[".files", "[]"]], ) -RootUploadFnInputFileFile = File +FileBf9b7 = File -RootUploadFnInputPathString = str - -RootUploadFnInputPathRootUploadFnInputPathStringOptional = typing.Union[ - RootUploadFnInputPathString, None -] +RootUploadFnInputPathString25e51Optional = typing.Union[str, None] RootUploadFnInput = typing.TypedDict( "RootUploadFnInput", { - "file": RootUploadFnInputFileFile, - "path": RootUploadFnInputPathRootUploadFnInputPathStringOptional, + "file": FileBf9b7, + "path": RootUploadFnInputPathString25e51Optional, }, total=False, ) -RootUploadManyFnInputPrefixRootUploadFnInputPathStringOptional = typing.Union[ - RootUploadFnInputPathString, None -] +RootUploadManyFnInputPrefixString25e51Optional = typing.Union[str, None] -RootUploadManyFnInputFilesRootUploadFnInputFileFileList = typing.List[ - RootUploadFnInputFileFile -] +RootUploadManyFnInputFilesFileBf9b7List = typing.List[FileBf9b7] RootUploadManyFnInput = typing.TypedDict( "RootUploadManyFnInput", { - "prefix": RootUploadManyFnInputPrefixRootUploadFnInputPathStringOptional, - "files": RootUploadManyFnInputFilesRootUploadFnInputFileFileList, + "prefix": RootUploadManyFnInputPrefixString25e51Optional, + "files": RootUploadManyFnInputFilesFileBf9b7List, }, total=False, ) -RootUploadFnOutput = bool - class QueryGraph(QueryGraphBase): def __init__(self): super().__init__( { - "RootUploadFnInputFileFile": "root_upload_fn_input_file_file!", - "RootUploadFnInputPathRootUploadFnInputPathStringOptional": "String", - "RootUploadManyFnInputPrefixRootUploadFnInputPathStringOptional": "String", - "RootUploadManyFnInputFilesRootUploadFnInputFileFileList": "[root_upload_fn_input_file_file]!", + "FileBf9b7": "file_bf9b7!", + "RootUploadFnInputPathString25e51Optional": "String", + "RootUploadManyFnInputPrefixString25e51Optional": "String", + "RootUploadManyFnInputFilesFileBf9b7List": "[file_bf9b7]!", } ) def upload( self, args: typing.Union[RootUploadFnInput, PlaceholderArgs] - ) -> MutationNode[RootUploadFnOutput]: + ) -> MutationNode[bool]: node = selection_to_nodes( {"upload": args}, {"upload": NodeDescs.RootUploadFn}, "$q" )[0] @@ -908,7 +898,7 @@ def upload( def upload_many( self, args: typing.Union[RootUploadManyFnInput, PlaceholderArgs] - ) -> MutationNode[RootUploadFnOutput]: + ) -> MutationNode[bool]: node = selection_to_nodes( {"uploadMany": args}, {"uploadMany": NodeDescs.RootUploadManyFn}, "$q" )[0] diff --git a/tests/metagen/typegraphs/sample/rs/client.rs b/tests/metagen/typegraphs/sample/rs/client.rs index 735fbba47..c7a9f3208 100644 --- a/tests/metagen/typegraphs/sample/rs/client.rs +++ b/tests/metagen/typegraphs/sample/rs/client.rs @@ -86,8 +86,8 @@ mod node_metas { arg_types: Some( [ ("id".into(), "UserIdStringUuid".into()), - ("slug".into(), "PostSlugString".into()), - ("title".into(), "PostSlugString".into()), + ("slug".into(), "StringE1a43".into()), + ("title".into(), "StringE1a43".into()), ].into() ), ..scalar() @@ -102,7 +102,7 @@ mod node_metas { NodeMeta { arg_types: Some( [ - ("id".into(), "PostSlugString".into()), + ("id".into(), "StringE1a43".into()), ].into() ), ..Post() @@ -112,7 +112,7 @@ mod node_metas { NodeMeta { arg_types: Some( [ - ("id".into(), "PostSlugString".into()), + ("id".into(), "StringE1a43".into()), ].into() ), ..scalar() @@ -135,7 +135,7 @@ mod node_metas { NodeMeta { arg_types: Some( [ - ("id".into(), "PostSlugString".into()), + ("id".into(), "StringE1a43".into()), ].into() ), ..RootCompositeUnionFnOutput() @@ -158,7 +158,7 @@ mod node_metas { NodeMeta { arg_types: Some( [ - ("id".into(), "PostSlugString".into()), + ("id".into(), "StringE1a43".into()), ].into() ), ..RootMixedUnionFnOutput() @@ -225,16 +225,15 @@ mod node_metas { use types::*; pub mod types { pub type UserIdStringUuid = String; - pub type PostSlugString = String; #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct PostPartial { pub id: Option, - pub slug: Option, - pub title: Option, + pub slug: Option, + pub title: Option, } #[derive(Debug, serde::Serialize, serde::Deserialize)] - pub struct RootCompositeArgsFnInputPartial { - pub id: Option, + pub struct StructC339cPartial { + pub id: Option, } pub type UserEmailStringEmail = String; pub type UserPostsPostList = Vec; @@ -244,12 +243,11 @@ pub mod types { pub email: Option, pub posts: Option, } - pub type RootScalarUnionFnOutputT1Integer = i64; #[derive(Debug, serde::Serialize, serde::Deserialize)] #[serde(untagged)] pub enum RootScalarUnionFnOutput { - PostSlugString(PostSlugString), - RootScalarUnionFnOutputT1Integer(RootScalarUnionFnOutputT1Integer), + String(String), + I64(i64), } #[derive(Debug, serde::Serialize, serde::Deserialize)] #[serde(untagged)] @@ -262,26 +260,26 @@ pub mod types { pub enum RootMixedUnionFnOutput { PostPartial(PostPartial), UserPartial(UserPartial), - PostSlugString(PostSlugString), - RootScalarUnionFnOutputT1Integer(RootScalarUnionFnOutputT1Integer), + String(String), + I64(i64), } #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct RootNestedCompositeFnOutputCompositeStructNestedStructPartial { - pub inner: Option, + pub inner: Option, } #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct RootNestedCompositeFnOutputCompositeStructPartial { - pub value: Option, + pub value: Option, pub nested: Option, } #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct RootNestedCompositeFnOutputListStructPartial { - pub value: Option, + pub value: Option, } pub type RootNestedCompositeFnOutputListRootNestedCompositeFnOutputListStructList = Vec; #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct RootNestedCompositeFnOutputPartial { - pub scalar: Option, + pub scalar: Option, pub composite: Option, pub list: Option, } @@ -344,7 +342,7 @@ impl QueryGraph { ty_to_gql_ty_map: std::sync::Arc::new([ ("UserIdStringUuid".into(), "String!".into()), - ("PostSlugString".into(), "String!".into()), + ("StringE1a43".into(), "String!".into()), ("post".into(), "post!".into()), ("user".into(), "user!".into()), ].into()), @@ -375,7 +373,7 @@ impl QueryGraph { } pub fn scalar_no_args( &self, - ) -> QueryNode + ) -> QueryNode { let nodes = selection_to_node_set( SelectionErasedMap( @@ -396,7 +394,7 @@ impl QueryGraph { pub fn scalar_args( &self, args: impl Into> - ) -> MutationNode + ) -> MutationNode { let nodes = selection_to_node_set( SelectionErasedMap( @@ -427,7 +425,7 @@ impl QueryGraph { } pub fn composite_args( &self, - args: impl Into> + args: impl Into> ) -> UnselectedNode, MutationMarker, PostPartial> { UnselectedNode { @@ -439,7 +437,7 @@ impl QueryGraph { } pub fn scalar_union( &self, - args: impl Into> + args: impl Into> ) -> QueryNode { let nodes = selection_to_node_set( @@ -460,7 +458,7 @@ impl QueryGraph { } pub fn composite_union( &self, - args: impl Into> + args: impl Into> ) -> UnselectedNode, QueryMarker, RootCompositeUnionFnOutput> { UnselectedNode { @@ -472,7 +470,7 @@ impl QueryGraph { } pub fn mixed_union( &self, - args: impl Into> + args: impl Into> ) -> UnselectedNode, QueryMarker, RootMixedUnionFnOutput> { UnselectedNode { diff --git a/tests/metagen/typegraphs/sample/rs/main.rs b/tests/metagen/typegraphs/sample/rs/main.rs index 7735f2f31..f12683e2c 100644 --- a/tests/metagen/typegraphs/sample/rs/main.rs +++ b/tests/metagen/typegraphs/sample/rs/main.rs @@ -37,8 +37,8 @@ fn main() -> Result<(), BoxErr> { ( api1.scalar_args(args.get("post", |val: types::PostPartial| val)), api1.composite_no_args().select(all()), - api1.composite_args(args.get("id", |id: String| { - types::RootCompositeArgsFnInputPartial { id: Some(id) } + api1.composite_args(args.get("id", |id: String| types::StructC339cPartial { + id: Some(id), })) .select(all()), ) @@ -98,7 +98,7 @@ fn main() -> Result<(), BoxErr> { title: Some("".into()), }), api1.composite_no_args().select(all()), - api1.composite_args(types::RootCompositeArgsFnInputPartial { + api1.composite_args(types::StructC339cPartial { id: Some("94be5420-8c4a-4e67-b4f4-e1b2b54832a2".into()), }) .select(all()), @@ -107,11 +107,11 @@ fn main() -> Result<(), BoxErr> { let res5 = gql .query(( - api1.scalar_union(types::RootCompositeArgsFnInputPartial { + api1.scalar_union(types::StructC339cPartial { id: Some("94be5420-8c4a-4e67-b4f4-e1b2b54832a2".into()), }), // allows ignoring some members - api1.composite_union(types::RootCompositeArgsFnInputPartial { + api1.composite_union(types::StructC339cPartial { id: Some("94be5420-8c4a-4e67-b4f4-e1b2b54832a2".into()), }) .select(RootCompositeUnionFnOutputSelections { @@ -120,14 +120,14 @@ fn main() -> Result<(), BoxErr> { }), // returns empty if returned type wasn't selected // in union member - api1.composite_union(types::RootCompositeArgsFnInputPartial { + api1.composite_union(types::StructC339cPartial { id: Some("94be5420-8c4a-4e67-b4f4-e1b2b54832a2".into()), }) .select(RootCompositeUnionFnOutputSelections { user: select(all()), ..default() }), - api1.mixed_union(types::RootCompositeArgsFnInputPartial { + api1.mixed_union(types::StructC339cPartial { id: Some("94be5420-8c4a-4e67-b4f4-e1b2b54832a2".into()), }) .select(RootMixedUnionFnOutputSelections { diff --git a/tests/metagen/typegraphs/sample/rs_upload/client.rs b/tests/metagen/typegraphs/sample/rs_upload/client.rs index 4b7e6b0ea..38b00263e 100644 --- a/tests/metagen/typegraphs/sample/rs_upload/client.rs +++ b/tests/metagen/typegraphs/sample/rs_upload/client.rs @@ -42,10 +42,10 @@ mod node_metas { NodeMeta { arg_types: Some( [ - ("file".into(), "RootUploadFnInputFileFile".into()), + ("file".into(), "FileBf9b7".into()), ( "path".into(), - "RootUploadFnInputPathRootUploadFnInputPathStringOptional".into(), + "RootUploadFnInputPathString25e51Optional".into(), ), ] .into(), @@ -62,11 +62,11 @@ mod node_metas { [ ( "prefix".into(), - "RootUploadManyFnInputPrefixRootUploadFnInputPathStringOptional".into(), + "RootUploadManyFnInputPrefixString25e51Optional".into(), ), ( "files".into(), - "RootUploadManyFnInputFilesRootUploadFnInputFileFileList".into(), + "RootUploadManyFnInputFilesFileBf9b7List".into(), ), ] .into(), @@ -81,25 +81,20 @@ mod node_metas { } use types::*; pub mod types { - pub type RootUploadFnInputFileFile = super::FileId; - pub type RootUploadFnInputPathString = String; - pub type RootUploadFnInputPathRootUploadFnInputPathStringOptional = - Option; + pub type FileBf9b7 = super::FileId; + pub type RootUploadFnInputPathString25e51Optional = Option; #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct RootUploadFnInputPartial { - pub file: Option, - pub path: RootUploadFnInputPathRootUploadFnInputPathStringOptional, + pub file: Option, + pub path: RootUploadFnInputPathString25e51Optional, } - pub type RootUploadManyFnInputPrefixRootUploadFnInputPathStringOptional = - Option; - pub type RootUploadManyFnInputFilesRootUploadFnInputFileFileList = - Vec; + pub type RootUploadManyFnInputPrefixString25e51Optional = Option; + pub type RootUploadManyFnInputFilesFileBf9b7List = Vec; #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct RootUploadManyFnInputPartial { - pub prefix: RootUploadManyFnInputPrefixRootUploadFnInputPathStringOptional, - pub files: Option, + pub prefix: RootUploadManyFnInputPrefixString25e51Optional, + pub files: Option, } - pub type RootUploadFnOutput = bool; } impl QueryGraph { @@ -108,21 +103,18 @@ impl QueryGraph { addr, ty_to_gql_ty_map: std::sync::Arc::new( [ + ("FileBf9b7".into(), "file_bf9b7!".into()), ( - "RootUploadFnInputFileFile".into(), - "root_upload_fn_input_file_file!".into(), - ), - ( - "RootUploadFnInputPathRootUploadFnInputPathStringOptional".into(), + "RootUploadFnInputPathString25e51Optional".into(), "String".into(), ), ( - "RootUploadManyFnInputPrefixRootUploadFnInputPathStringOptional".into(), + "RootUploadManyFnInputPrefixString25e51Optional".into(), "String".into(), ), ( - "RootUploadManyFnInputFilesRootUploadFnInputFileFileList".into(), - "[root_upload_fn_input_file_file]!".into(), + "RootUploadManyFnInputFilesFileBf9b7List".into(), + "[file_bf9b7]!".into(), ), ] .into(), @@ -133,7 +125,7 @@ impl QueryGraph { pub fn upload( &self, args: impl Into>, - ) -> MutationNode { + ) -> MutationNode { let nodes = selection_to_node_set( SelectionErasedMap( [( @@ -151,7 +143,7 @@ impl QueryGraph { pub fn upload_many( &self, args: impl Into>, - ) -> MutationNode { + ) -> MutationNode { let nodes = selection_to_node_set( SelectionErasedMap( [( diff --git a/tests/metagen/typegraphs/sample/ts/client.ts b/tests/metagen/typegraphs/sample/ts/client.ts index fb796ffe7..fb0fe7aa6 100644 --- a/tests/metagen/typegraphs/sample/ts/client.ts +++ b/tests/metagen/typegraphs/sample/ts/client.ts @@ -916,8 +916,8 @@ const nodeMetas = { ...nodeMetas.scalar(), argumentTypes: { id: "UserIdStringUuid", - slug: "PostSlugString", - title: "PostSlugString", + slug: "StringE1a43", + title: "StringE1a43", }, }; }, @@ -930,7 +930,7 @@ const nodeMetas = { return { ...nodeMetas.Post(), argumentTypes: { - id: "PostSlugString", + id: "StringE1a43", }, }; }, @@ -938,7 +938,7 @@ const nodeMetas = { return { ...nodeMetas.scalar(), argumentTypes: { - id: "PostSlugString", + id: "StringE1a43", }, }; }, @@ -954,7 +954,7 @@ const nodeMetas = { return { ...nodeMetas.RootCompositeUnionFnOutput(), argumentTypes: { - id: "PostSlugString", + id: "StringE1a43", }, }; }, @@ -970,7 +970,7 @@ const nodeMetas = { return { ...nodeMetas.RootMixedUnionFnOutput(), argumentTypes: { - id: "PostSlugString", + id: "StringE1a43", }, }; }, @@ -1012,14 +1012,13 @@ const nodeMetas = { }, }; export type UserIdStringUuid = string; -export type PostSlugString = string; export type Post = { id: UserIdStringUuid; - slug: PostSlugString; - title: PostSlugString; + slug: string; + title: string; }; -export type RootCompositeArgsFnInput = { - id: PostSlugString; +export type StructC339c = { + id: string; }; export type UserEmailStringEmail = string; export type UserPostsPostList = Array; @@ -1028,31 +1027,30 @@ export type User = { email: UserEmailStringEmail; posts: UserPostsPostList; }; -export type RootScalarUnionFnOutputT1Integer = number; export type RootScalarUnionFnOutput = - | (PostSlugString) - | (RootScalarUnionFnOutputT1Integer); + | (string) + | (number); export type RootCompositeUnionFnOutput = | (Post) | (User); export type RootMixedUnionFnOutput = | (Post) | (User) - | (PostSlugString) - | (RootScalarUnionFnOutputT1Integer); + | (string) + | (number); export type RootNestedCompositeFnOutputCompositeStructNestedStruct = { - inner: RootScalarUnionFnOutputT1Integer; + inner: number; }; export type RootNestedCompositeFnOutputCompositeStruct = { - value: RootScalarUnionFnOutputT1Integer; + value: number; nested: RootNestedCompositeFnOutputCompositeStructNestedStruct; }; export type RootNestedCompositeFnOutputListStruct = { - value: RootScalarUnionFnOutputT1Integer; + value: number; }; export type RootNestedCompositeFnOutputListRootNestedCompositeFnOutputListStructList = Array; export type RootNestedCompositeFnOutput = { - scalar: RootScalarUnionFnOutputT1Integer; + scalar: number; composite: RootNestedCompositeFnOutputCompositeStruct; list: RootNestedCompositeFnOutputListRootNestedCompositeFnOutputListStructList; }; @@ -1103,7 +1101,7 @@ export class QueryGraph extends _QueryGraphBase { constructor() { super({ "UserIdStringUuid": "String!", - "PostSlugString": "String!", + "StringE1a43": "String!", "post": "post!", "user": "user!", }); @@ -1131,7 +1129,7 @@ export class QueryGraph extends _QueryGraphBase { [["scalarNoArgs", nodeMetas.RootScalarNoArgsFn]], "$q", )[0]; - return new QueryNode(inner) as QueryNode; + return new QueryNode(inner) as QueryNode; } scalarArgs(args: Post | PlaceholderArgs) { const inner = _selectionToNodeSet( @@ -1139,7 +1137,7 @@ export class QueryGraph extends _QueryGraphBase { [["scalarArgs", nodeMetas.RootScalarArgsFn]], "$q", )[0]; - return new MutationNode(inner) as MutationNode; + return new MutationNode(inner) as MutationNode; } compositeNoArgs(select: PostSelections) { const inner = _selectionToNodeSet( @@ -1149,7 +1147,7 @@ export class QueryGraph extends _QueryGraphBase { )[0]; return new MutationNode(inner) as MutationNode; } - compositeArgs(args: RootCompositeArgsFnInput | PlaceholderArgs, select: PostSelections) { + compositeArgs(args: StructC339c | PlaceholderArgs, select: PostSelections) { const inner = _selectionToNodeSet( { "compositeArgs": [args, select] }, [["compositeArgs", nodeMetas.RootCompositeArgsFn]], @@ -1157,7 +1155,7 @@ export class QueryGraph extends _QueryGraphBase { )[0]; return new MutationNode(inner) as MutationNode; } - scalarUnion(args: RootCompositeArgsFnInput | PlaceholderArgs) { + scalarUnion(args: StructC339c | PlaceholderArgs) { const inner = _selectionToNodeSet( { "scalarUnion": args }, [["scalarUnion", nodeMetas.RootScalarUnionFn]], @@ -1165,7 +1163,7 @@ export class QueryGraph extends _QueryGraphBase { )[0]; return new QueryNode(inner) as QueryNode; } - compositeUnion(args: RootCompositeArgsFnInput | PlaceholderArgs, select: RootCompositeUnionFnOutputSelections) { + compositeUnion(args: StructC339c | PlaceholderArgs, select: RootCompositeUnionFnOutputSelections) { const inner = _selectionToNodeSet( { "compositeUnion": [args, select] }, [["compositeUnion", nodeMetas.RootCompositeUnionFn]], @@ -1173,7 +1171,7 @@ export class QueryGraph extends _QueryGraphBase { )[0]; return new QueryNode(inner) as QueryNode; } - mixedUnion(args: RootCompositeArgsFnInput | PlaceholderArgs, select: RootMixedUnionFnOutputSelections) { + mixedUnion(args: StructC339c | PlaceholderArgs, select: RootMixedUnionFnOutputSelections) { const inner = _selectionToNodeSet( { "mixedUnion": [args, select] }, [["mixedUnion", nodeMetas.RootMixedUnionFn]], diff --git a/tests/metagen/typegraphs/sample/ts_upload/client.ts b/tests/metagen/typegraphs/sample/ts_upload/client.ts index bafdb967b..faa9a07de 100644 --- a/tests/metagen/typegraphs/sample/ts_upload/client.ts +++ b/tests/metagen/typegraphs/sample/ts_upload/client.ts @@ -882,8 +882,8 @@ const nodeMetas = { return { ...nodeMetas.scalar(), argumentTypes: { - file: "RootUploadFnInputFileFile", - path: "RootUploadFnInputPathRootUploadFnInputPathStringOptional", + file: "FileBf9b7", + path: "RootUploadFnInputPathString25e51Optional", }, inputFiles: [[".file"]], }; @@ -892,36 +892,34 @@ const nodeMetas = { return { ...nodeMetas.scalar(), argumentTypes: { - prefix: "RootUploadManyFnInputPrefixRootUploadFnInputPathStringOptional", - files: "RootUploadManyFnInputFilesRootUploadFnInputFileFileList", + prefix: "RootUploadManyFnInputPrefixString25e51Optional", + files: "RootUploadManyFnInputFilesFileBf9b7List", }, inputFiles: [[".files","[]"]], }; }, }; -export type RootUploadFnInputFileFile = File; -export type RootUploadFnInputPathString = string; -export type RootUploadFnInputPathRootUploadFnInputPathStringOptional = RootUploadFnInputPathString | null | undefined; +export type FileBf9b7 = File; +export type RootUploadFnInputPathString25e51Optional = string | null | undefined; export type RootUploadFnInput = { - file: RootUploadFnInputFileFile; - path?: RootUploadFnInputPathRootUploadFnInputPathStringOptional; + file: FileBf9b7; + path?: RootUploadFnInputPathString25e51Optional; }; -export type RootUploadManyFnInputPrefixRootUploadFnInputPathStringOptional = RootUploadFnInputPathString | null | undefined; -export type RootUploadManyFnInputFilesRootUploadFnInputFileFileList = Array; +export type RootUploadManyFnInputPrefixString25e51Optional = string | null | undefined; +export type RootUploadManyFnInputFilesFileBf9b7List = Array; export type RootUploadManyFnInput = { - prefix?: RootUploadManyFnInputPrefixRootUploadFnInputPathStringOptional; - files: RootUploadManyFnInputFilesRootUploadFnInputFileFileList; + prefix?: RootUploadManyFnInputPrefixString25e51Optional; + files: RootUploadManyFnInputFilesFileBf9b7List; }; -export type RootUploadFnOutput = boolean; export class QueryGraph extends _QueryGraphBase { constructor() { super({ - "RootUploadFnInputFileFile": "root_upload_fn_input_file_file!", - "RootUploadFnInputPathRootUploadFnInputPathStringOptional": "String", - "RootUploadManyFnInputPrefixRootUploadFnInputPathStringOptional": "String", - "RootUploadManyFnInputFilesRootUploadFnInputFileFileList": "[root_upload_fn_input_file_file]!", + "FileBf9b7": "file_bf9b7!", + "RootUploadFnInputPathString25e51Optional": "String", + "RootUploadManyFnInputPrefixString25e51Optional": "String", + "RootUploadManyFnInputFilesFileBf9b7List": "[file_bf9b7]!", }); } @@ -931,7 +929,7 @@ export class QueryGraph extends _QueryGraphBase { [["upload", nodeMetas.RootUploadFn]], "$q", )[0]; - return new MutationNode(inner) as MutationNode; + return new MutationNode(inner) as MutationNode; } uploadMany(args: RootUploadManyFnInput | PlaceholderArgs) { const inner = _selectionToNodeSet( @@ -939,6 +937,6 @@ export class QueryGraph extends _QueryGraphBase { [["uploadMany", nodeMetas.RootUploadManyFn]], "$q", )[0]; - return new MutationNode(inner) as MutationNode; + return new MutationNode(inner) as MutationNode; } } diff --git a/tests/planner/__snapshots__/planner_test.ts.snap b/tests/planner/__snapshots__/planner_test.ts.snap index 3a67182e5..b726cd48e 100644 --- a/tests/planner/__snapshots__/planner_test.ts.snap +++ b/tests/planner/__snapshots__/planner_test.ts.snap @@ -35,7 +35,7 @@ snapshot[`planner 1`] = ` ], type: { format: "email", - title: "root_one_fn_output_email_string_email", + title: "string_email_1b9c2", type: "string", }, }, @@ -60,7 +60,7 @@ snapshot[`planner 1`] = ` "first", ], type: { - title: "root_one_fn_output_nested_struct_first_string", + title: "string_cbe33", type: "string", }, }, @@ -170,110 +170,110 @@ snapshot[`planner 2`] = ` snapshot[`planner 3`] = ` [ "one one one object root_one_fn_output false", - "one.union1 union1 one/union1 union root_one_fn_output_union1_union false", - "one.union2 union2 one/union2 union root_one_fn_output_union2_union false", - "one.union2\$A.a a one/union2\$A/a integer root_one_fn_output_union1_union_t0_integer false", + "one.union1 union1 one/union1 union union_aa854 false", + "one.union2 union2 one/union2 union union_2f7ce false", + "one.union2\$A.a a one/union2\$A/a integer integer_6d74d false", "one.union2\$B.b b one/union2\$B/b either B_b_either false", - "one.union2\$B.b\$C1.c c one/union2\$B/b\$C1/c integer root_one_fn_output_union1_union_t0_integer false", - "one.union2\$B.b\$C2.c c one/union2\$B/b\$C2/c string root_one_fn_output_nested_struct_first_string false", + "one.union2\$B.b\$C1.c c one/union2\$B/b\$C1/c integer integer_6d74d false", + "one.union2\$B.b\$C2.c c one/union2\$B/b\$C2/c string string_cbe33 false", ] `; snapshot[`planner 4`] = ` [ "one one one object root_one_fn_output false", - "one.union2 union2 one/union2 union root_one_fn_output_union2_union true", - "one.union2\$A.a a one/union2\$A/a integer root_one_fn_output_union1_union_t0_integer false", + "one.union2 union2 one/union2 union union_2f7ce true", + "one.union2\$A.a a one/union2\$A/a integer integer_6d74d false", "one.union2\$B.b b one/union2\$B/b either B_b_either false", - "one.union2\$B.b\$C1.c c one/union2\$B/b\$C1/c integer root_one_fn_output_union1_union_t0_integer false", - "one.union2\$B.b\$C2.c c one/union2\$B/b\$C2/c string root_one_fn_output_nested_struct_first_string false", - "one.from_union2 from_union2 one/from_union2 integer root_one_fn_output_union1_union_t0_integer false", - "one.union1 union1 one/union1 union root_one_fn_output_union1_union true", - "one.from_union1 from_union1 one/from_union1 integer root_one_fn_output_union1_union_t0_integer false", + "one.union2\$B.b\$C1.c c one/union2\$B/b\$C1/c integer integer_6d74d false", + "one.union2\$B.b\$C2.c c one/union2\$B/b\$C2/c string string_cbe33 false", + "one.from_union2 from_union2 one/from_union2 integer integer_6d74d false", + "one.union1 union1 one/union1 union union_aa854 true", + "one.from_union1 from_union1 one/from_union1 integer integer_6d74d false", ] `; snapshot[`planner: dependencies 1`] = ` [ "two two two object root_two_fn_output false", - "two.id id two/id integer root_one_fn_output_union1_union_t0_integer false", - "two.email email two/email string root_one_fn_output_email_string_email false", + "two.id id two/id integer integer_6d74d false", + "two.email email two/email string string_email_1b9c2 false", ] `; snapshot[`planner: dependencies 2`] = ` [ "two two two object root_two_fn_output false", - "two.id id two/id integer root_one_fn_output_union1_union_t0_integer false", - "two.email email two/email string root_one_fn_output_email_string_email false", - "two.profile profile two/profile object root_two_fn_output_profile_fn_output false", - "two.profile.firstName firstName two/profile/firstName string root_one_fn_output_nested_struct_first_string false", - "two.profile.lastName lastName two/profile/lastName string root_one_fn_output_nested_struct_first_string false", - "two.profile.profilePic profilePic two/profile/profilePic string root_one_fn_output_nested_struct_first_string false", + "two.id id two/id integer integer_6d74d false", + "two.email email two/email string string_email_1b9c2 false", + "two.profile profile two/profile object struct_c9440 false", + "two.profile.firstName firstName two/profile/firstName string string_cbe33 false", + "two.profile.lastName lastName two/profile/lastName string string_cbe33 false", + "two.profile.profilePic profilePic two/profile/profilePic string string_cbe33 false", ] `; snapshot[`planner: dependencies 3`] = ` [ "two two two object root_two_fn_output false", - "two.id id two/id integer root_one_fn_output_union1_union_t0_integer false", - "two.email email two/email string root_one_fn_output_email_string_email false", - "two.profile profile two/profile object root_two_fn_output_profile_fn_output false", - "two.profile.firstName firstName two/profile/firstName string root_one_fn_output_nested_struct_first_string false", - "two.profile.lastName lastName two/profile/lastName string root_one_fn_output_nested_struct_first_string false", - "two.profile.profilePic profilePic two/profile/profilePic string root_one_fn_output_nested_struct_first_string false", + "two.id id two/id integer integer_6d74d false", + "two.email email two/email string string_email_1b9c2 false", + "two.profile profile two/profile object struct_c9440 false", + "two.profile.firstName firstName two/profile/firstName string string_cbe33 false", + "two.profile.lastName lastName two/profile/lastName string string_cbe33 false", + "two.profile.profilePic profilePic two/profile/profilePic string string_cbe33 false", ] `; snapshot[`planner: dependencies 4`] = ` [ "two two two object root_two_fn_output false", - "two.email email two/email string root_one_fn_output_email_string_email true", - "two.profile profile two/profile object root_two_fn_output_profile_fn_output false", - "two.profile.firstName firstName two/profile/firstName string root_one_fn_output_nested_struct_first_string false", - "two.profile.lastName lastName two/profile/lastName string root_one_fn_output_nested_struct_first_string false", - "two.profile.profilePic profilePic two/profile/profilePic string root_one_fn_output_nested_struct_first_string false", - "two.id id two/id integer root_one_fn_output_union1_union_t0_integer false", + "two.email email two/email string string_email_1b9c2 true", + "two.profile profile two/profile object struct_c9440 false", + "two.profile.firstName firstName two/profile/firstName string string_cbe33 false", + "two.profile.lastName lastName two/profile/lastName string string_cbe33 false", + "two.profile.profilePic profilePic two/profile/profilePic string string_cbe33 false", + "two.id id two/id integer integer_6d74d false", ] `; snapshot[`planner: dependencies 5`] = ` [ "two two two object root_two_fn_output false", - "two.email email two/email string root_one_fn_output_email_string_email true", - "two.profile profile two/profile object root_two_fn_output_profile_fn_output false", - "two.profile.firstName firstName two/profile/firstName string root_one_fn_output_nested_struct_first_string false", - "two.profile.lastName lastName two/profile/lastName string root_one_fn_output_nested_struct_first_string false", - "two.profile.profilePic profilePic two/profile/profilePic string root_one_fn_output_nested_struct_first_string false", - "two.taggedPic taggedPic two/taggedPic string root_one_fn_output_nested_struct_first_string false", + "two.email email two/email string string_email_1b9c2 true", + "two.profile profile two/profile object struct_c9440 false", + "two.profile.firstName firstName two/profile/firstName string string_cbe33 false", + "two.profile.lastName lastName two/profile/lastName string string_cbe33 false", + "two.profile.profilePic profilePic two/profile/profilePic string string_cbe33 false", + "two.taggedPic taggedPic two/taggedPic string string_cbe33 false", ] `; snapshot[`planner: dependencies 6`] = ` [ "two two two object root_two_fn_output false", - "two.id id two/id integer root_one_fn_output_union1_union_t0_integer false", - "two.email email two/email string root_one_fn_output_email_string_email true", - "two.profile profile two/profile object root_two_fn_output_profile_fn_output true", - "two.profile.email email two/profile/email string root_one_fn_output_email_string_email false", - "two.profile.firstName firstName two/profile/firstName string root_one_fn_output_nested_struct_first_string false", - "two.profile.lastName lastName two/profile/lastName string root_one_fn_output_nested_struct_first_string false", - "two.profile.profilePic profilePic two/profile/profilePic string root_one_fn_output_nested_struct_first_string false", - "two.taggedPic taggedPic two/taggedPic string root_one_fn_output_nested_struct_first_string false", + "two.id id two/id integer integer_6d74d false", + "two.email email two/email string string_email_1b9c2 true", + "two.profile profile two/profile object struct_c9440 true", + "two.profile.email email two/profile/email string string_email_1b9c2 false", + "two.profile.firstName firstName two/profile/firstName string string_cbe33 false", + "two.profile.lastName lastName two/profile/lastName string string_cbe33 false", + "two.profile.profilePic profilePic two/profile/profilePic string string_cbe33 false", + "two.taggedPic taggedPic two/taggedPic string string_cbe33 false", ] `; snapshot[`planner: dependencies in union/either 1`] = ` [ "three three three object root_three_fn_output false", - "three.id id three/id integer root_one_fn_output_union1_union_t0_integer false", + "three.id id three/id integer integer_6d74d false", "three.user user three/user either root_three_fn_output_user_either false", "three.user\$RegisteredUser.id id three/user\$RegisteredUser/id string root_one_fn_output_id_string_uuid false", - "three.user\$RegisteredUser.email email three/user\$RegisteredUser/email string root_one_fn_output_email_string_email false", + "three.user\$RegisteredUser.email email three/user\$RegisteredUser/email string string_email_1b9c2 false", "three.user\$RegisteredUser.profile profile three/user\$RegisteredUser/profile object RegisteredUser_profile_fn_output false", - "three.user\$RegisteredUser.profile.email email three/user\$RegisteredUser/profile/email string root_one_fn_output_email_string_email false", - "three.user\$RegisteredUser.profile.displayName displayName three/user\$RegisteredUser/profile/displayName string root_one_fn_output_nested_struct_first_string false", - "three.user\$RegisteredUser.profile.profilePic profilePic three/user\$RegisteredUser/profile/profilePic string root_one_fn_output_nested_struct_first_string false", + "three.user\$RegisteredUser.profile.email email three/user\$RegisteredUser/profile/email string string_email_1b9c2 false", + "three.user\$RegisteredUser.profile.displayName displayName three/user\$RegisteredUser/profile/displayName string string_cbe33 false", + "three.user\$RegisteredUser.profile.profilePic profilePic three/user\$RegisteredUser/profile/profilePic string string_cbe33 false", "three.user\$GuestUser.id id three/user\$GuestUser/id string root_one_fn_output_id_string_uuid false", ] `; @@ -281,13 +281,13 @@ snapshot[`planner: dependencies in union/either 1`] = ` snapshot[`planner: dependencies in union/either 2`] = ` [ "three three three object root_three_fn_output false", - "three.id id three/id integer root_one_fn_output_union1_union_t0_integer false", + "three.id id three/id integer integer_6d74d false", "three.user user three/user either root_three_fn_output_user_either false", "three.user\$RegisteredUser.id id three/user\$RegisteredUser/id string root_one_fn_output_id_string_uuid false", - "three.user\$RegisteredUser.email email three/user\$RegisteredUser/email string root_one_fn_output_email_string_email true", + "three.user\$RegisteredUser.email email three/user\$RegisteredUser/email string string_email_1b9c2 true", "three.user\$RegisteredUser.profile profile three/user\$RegisteredUser/profile object RegisteredUser_profile_fn_output false", - "three.user\$RegisteredUser.profile.email email three/user\$RegisteredUser/profile/email string root_one_fn_output_email_string_email false", - "three.user\$RegisteredUser.profile.displayName displayName three/user\$RegisteredUser/profile/displayName string root_one_fn_output_nested_struct_first_string false", + "three.user\$RegisteredUser.profile.email email three/user\$RegisteredUser/profile/email string string_email_1b9c2 false", + "three.user\$RegisteredUser.profile.displayName displayName three/user\$RegisteredUser/profile/displayName string string_cbe33 false", "three.user\$GuestUser.id id three/user\$GuestUser/id string root_one_fn_output_id_string_uuid false", ] `; diff --git a/tests/runtimes/deno/deno_sync_test.ts b/tests/runtimes/deno/deno_sync_test.ts index 79688aef9..e4e285961 100644 --- a/tests/runtimes/deno/deno_sync_test.ts +++ b/tests/runtimes/deno/deno_sync_test.ts @@ -67,27 +67,28 @@ Meta.test( .on(e); }); - await t.should("work with global variables in a module", async () => { - await gql` - mutation { - count - } - ` - .expectData({ - count: 1, - }) - .on(e); - - await gql` - mutation { - count - } - ` - .expectData({ - count: 2, - }) - .on(e); - }); + // -- no worker reuse... + // await t.should("work with global variables in a module", async () => { + // await gql` + // mutation { + // count + // } + // ` + // .expectData({ + // count: 1, + // }) + // .on(e); + // + // await gql` + // mutation { + // count + // } + // ` + // .expectData({ + // count: 2, + // }) + // .on(e); + // }); await t.should("work with async function", async () => { await gql` diff --git a/tests/runtimes/deno/deno_test.ts b/tests/runtimes/deno/deno_test.ts index b0d14dce4..70a0a4de7 100644 --- a/tests/runtimes/deno/deno_test.ts +++ b/tests/runtimes/deno/deno_test.ts @@ -39,27 +39,28 @@ Meta.test( .on(e); }); - await t.should("work with global variables in a module", async () => { - await gql` - mutation { - count - } - ` - .expectData({ - count: 1, - }) - .on(e); - - await gql` - mutation { - count - } - ` - .expectData({ - count: 2, - }) - .on(e); - }); + // -- no worker reuse... + // await t.should("work with global variables in a module", async () => { + // await gql` + // mutation { + // count + // } + // ` + // .expectData({ + // count: 1, + // }) + // .on(e); + // + // await gql` + // mutation { + // count + // } + // ` + // .expectData({ + // count: 2, + // }) + // .on(e); + // }); await t.should("work with async function", async () => { await gql` diff --git a/tests/runtimes/grpc/__snapshots__/grpc_test.ts.snap b/tests/runtimes/grpc/__snapshots__/grpc_test.ts.snap index c8f82853f..9451e12f2 100644 --- a/tests/runtimes/grpc/__snapshots__/grpc_test.ts.snap +++ b/tests/runtimes/grpc/__snapshots__/grpc_test.ts.snap @@ -44,7 +44,7 @@ snapshot[`Typegraph using grpc 1`] = ` }, { "type": "optional", - "title": "root_greet_fn_input_name_root_greet_fn_input_name_string_optional", + "title": "root_greet_fn_input_name_string_optional_b02c2", "item": 4, "default_value": null }, @@ -180,7 +180,7 @@ snapshot[`Typegraph using grpc 2`] = ` }, { "type": "optional", - "title": "root_greet_fn_input_name_root_greet_fn_input_name_string_optional", + "title": "root_greet_fn_input_name_string_optional_b02c2", "item": 4, "default_value": null }, diff --git a/tests/runtimes/kv/__snapshots__/kv_test.ts.snap b/tests/runtimes/kv/__snapshots__/kv_test.ts.snap index 948e9875a..4af57a581 100644 --- a/tests/runtimes/kv/__snapshots__/kv_test.ts.snap +++ b/tests/runtimes/kv/__snapshots__/kv_test.ts.snap @@ -52,7 +52,7 @@ snapshot[`Typegraph using kv 1`] = ` }, { "type": "object", - "title": "root_get_fn_input", + "title": "struct_d0873", "properties": { "key": 3 }, @@ -64,11 +64,11 @@ snapshot[`Typegraph using kv 1`] = ` }, { "type": "string", - "title": "root_get_fn_input_key_string" + "title": "string_a7e0d" }, { "type": "optional", - "title": "root_get_fn_output", + "title": "string_a7e0d_optional_d0873", "item": 3, "default_value": null }, @@ -122,7 +122,7 @@ snapshot[`Typegraph using kv 1`] = ` }, { "type": "object", - "title": "root_keys_fn_input", + "title": "struct_443be", "properties": { "filter": 4 }, @@ -134,7 +134,7 @@ snapshot[`Typegraph using kv 1`] = ` }, { "type": "list", - "title": "root_keys_fn_output", + "title": "string_a7e0d_list_443be", "items": 3 }, { @@ -304,7 +304,7 @@ snapshot[`Typegraph using kv 2`] = ` }, { "type": "object", - "title": "root_get_fn_input", + "title": "struct_d0873", "properties": { "key": 3 }, @@ -316,11 +316,11 @@ snapshot[`Typegraph using kv 2`] = ` }, { "type": "string", - "title": "root_get_fn_input_key_string" + "title": "string_a7e0d" }, { "type": "optional", - "title": "root_get_fn_output", + "title": "string_a7e0d_optional_d0873", "item": 3, "default_value": null }, @@ -374,7 +374,7 @@ snapshot[`Typegraph using kv 2`] = ` }, { "type": "object", - "title": "root_keys_fn_input", + "title": "struct_443be", "properties": { "filter": 4 }, @@ -386,7 +386,7 @@ snapshot[`Typegraph using kv 2`] = ` }, { "type": "list", - "title": "root_keys_fn_output", + "title": "string_a7e0d_list_443be", "items": 3 }, { diff --git a/tests/runtimes/prisma/type_duplication.ts b/tests/runtimes/prisma/type_duplication.ts index 463ab3cd3..330c93116 100644 --- a/tests/runtimes/prisma/type_duplication.ts +++ b/tests/runtimes/prisma/type_duplication.ts @@ -6,52 +6,70 @@ import { PrismaRuntime } from "@typegraph/sdk/providers/prisma"; export const tg = await typegraph("type-duplication", (g: any) => { const prisma = new PrismaRuntime("prisma", "POSTGRES"); - const entts = { - vivaSession: t.struct({ - id: t.uuid({ config: ["auto"] }).id(), - sourceLink: g.ref("scenarioLink"), - // response: g.ref("response").optional(), - }), - scenario: t.struct({ - id: t.uuid({ config: ["auto"] }).id(), - scenes: t.list(g.ref("scene")), - links: t.list(g.ref("scenarioLink")), - }), - scenarioLink: t.struct({ - id: t.uuid({ config: ["auto"] }).id(), - scenario: g.ref("scenario"), - sessions: t.list(g.ref("vivaSession")), - }), - scene: t.struct({ - id: t.uuid({ config: ["auto"] }).id(), - scenario: g.ref("scenario"), - video: g.ref("sceneVideo").optional(), - responseVideos: t.list(g.ref("responseVideo")), - }), - sceneVideo: t.struct({ - id: t.uuid({ config: ["auto"] }).id(), - scene: g.ref("scene"), - }), - response: t.struct({ - id: t.uuid({ config: ["auto"] }).id(), - // session: g.ref("vivaSession"), - videos: t.list(g.ref("responseVideo")), - }), - responseVideo: t.struct({ - id: t.uuid({ config: ["auto"] }).id(), - response: g.ref("response"), - scene: g.ref("scene"), - }), - } as Record; + const noOfTypes = Number(Deno.env.get("TYPE_COUNT") ?? 5); + if (Number.isNaN(noOfTypes)) { + throw new Error("NAN!"); + } + const enttNames = [...Array(noOfTypes).keys()].map((ii) => `entt${ii}`); + const entts = Object.fromEntries(enttNames.map( + (name, ii) => { + const fields = { + id: t.uuid().id(), + } as Record; + let jj = -1; + for (const ref of enttNames) { + jj += 1; + + // skip self on self + if (ii == jj) continue; + + // both are even or both are odd + // evens have single optional evens + // odds have single optional odds + if (ii % 2 == jj % 2) { + fields[`field_${ref}`] = ii < jj + ? t.optional(g.ref(ref)) + // latter numbers should have the fkey + : prisma.link( + t.optional(g.ref(ref)), + `link_${ii}_to_${jj}`, + { fkey: true }, + ); + } // + // ref is odd + // evens have multiple odds + else if (ii % 2 == 0 && jj % 2 != 0) { + fields[`field_${ref}`] = t.list(g.ref(ref)); + } // + // odds have a single even + // self is odd + else { + fields[`field_${ref}`] = g.ref(ref); + } + } + return [ + name, + t.struct(fields), + ]; + }, + )) as Record; const rootFns = {} as Record; for (const [key, type] of Object.entries(entts)) { entts[key] = (type as t.Typedef).rename(key); } for (const [key, type] of Object.entries(entts)) { rootFns[`find_${key}`] = prisma.findFirst(type); + rootFns[`find_unique_${key}`] = prisma.findUnique(type); + rootFns[`find_many_${key}`] = prisma.findMany(type); rootFns[`create_${key}`] = prisma.create(type); + rootFns[`create_many_${key}`] = prisma.createMany(type); rootFns[`update_${key}`] = prisma.update(type); - rootFns[`delte_${key}`] = prisma.delete(type); + rootFns[`update_many_${key}`] = prisma.updateMany(type); + rootFns[`delete_${key}`] = prisma.delete(type); + rootFns[`delete_many_${key}`] = prisma.deleteMany(type); + rootFns[`upsert_${key}`] = prisma.upsert(type); + rootFns[`aggregate_${key}`] = prisma.aggregate(type); + rootFns[`group_by_${key}`] = prisma.groupBy(type); } g.expose( { diff --git a/tests/runtimes/prisma/type_duplication_test.ts b/tests/runtimes/prisma/type_duplication_test.ts index 894f3b933..8bee5da1e 100644 --- a/tests/runtimes/prisma/type_duplication_test.ts +++ b/tests/runtimes/prisma/type_duplication_test.ts @@ -9,16 +9,16 @@ Meta.test("serialization size test", async (mt) => { const raw = await mt.serialize("runtimes/prisma/type_duplication.ts"); const size = new TextEncoder().encode(raw).length; assert( - size < 1_240_000, + size < (1_240_000 * 8), `serialized size is too large ${Math.ceil(size / 1024)}KiB`, ); - console.log(raw) + console.log(raw); const tg: TypeGraphDS = JSON.parse( raw, ); assert( - tg.types.length < 3000, + tg.types.length < 30_000, `typegraph has too many types: ${tg.types.length}`, ); }); diff --git a/tests/runtimes/s3/__snapshots__/s3_test.ts.snap b/tests/runtimes/s3/__snapshots__/s3_test.ts.snap index 48f5b3fdc..511b87ffe 100644 --- a/tests/runtimes/s3/__snapshots__/s3_test.ts.snap +++ b/tests/runtimes/s3/__snapshots__/s3_test.ts.snap @@ -63,13 +63,13 @@ snapshot[`s3 typegraphs 1`] = ` }, { "type": "optional", - "title": "root_listObjects_fn_input_path_root_listObjects_fn_input_path_string_optional", + "title": "string_7c0c4_optional_7c0c4", "item": 4, "default_value": null }, { "type": "string", - "title": "root_listObjects_fn_input_path_string" + "title": "string_7c0c4" }, { "type": "object", @@ -106,11 +106,11 @@ snapshot[`s3 typegraphs 1`] = ` }, { "type": "integer", - "title": "root_listObjects_fn_output_keys_struct_size_integer" + "title": "integer_d5974" }, { "type": "list", - "title": "root_listObjects_fn_output_prefix_root_listObjects_fn_input_path_string_list", + "title": "root_listObjects_fn_output_prefix_string_7c0c4_list", "items": 4 }, { @@ -137,7 +137,7 @@ snapshot[`s3 typegraphs 1`] = ` }, { "type": "string", - "title": "root_getDownloadUrl_fn_output", + "title": "string_uri_99c36", "format": "uri" }, { @@ -197,7 +197,7 @@ snapshot[`s3 typegraphs 1`] = ` }, { "type": "boolean", - "title": "root_upload_fn_output" + "title": "boolean_02b82" }, { "type": "function", @@ -225,7 +225,7 @@ snapshot[`s3 typegraphs 1`] = ` }, { "type": "optional", - "title": "root_uploadMany_fn_input_prefix_root_listObjects_fn_input_path_string_optional", + "title": "root_uploadMany_fn_input_prefix_string_7c0c4_optional", "item": 4, "default_value": "" }, diff --git a/tests/runtimes/substantial/common.ts b/tests/runtimes/substantial/common.ts index 589b18de2..cbf0b2a58 100644 --- a/tests/runtimes/substantial/common.ts +++ b/tests/runtimes/substantial/common.ts @@ -26,22 +26,30 @@ export function redisCleanup(url: string) { }; } +type TestTemplateOptions = { + secrets?: Record; + only?: boolean; +}; + +type TestTemplateOptionsX = TestTemplateOptions & { + delays: { + [K in TDelayKeys]: number; + }; +}; + export function basicTestTemplate( backendName: BackendName, { delays, secrets, - }: { - delays: { - awaitSleepCompleteSec: number; - }; - secrets?: Record; - }, + only = false, + }: TestTemplateOptionsX<"awaitSleepCompleteSec">, cleanup?: MetaTestCleanupFn, ) { Meta.test( { name: `Basic workflow execution lifecycle + interrupts (${backendName})`, + only, }, async (t) => { Deno.env.set("SUB_BACKEND", backendName); @@ -175,17 +183,14 @@ export function concurrentWorkflowTestTemplate( { delays, secrets, - }: { - delays: { - awaitEmailCompleteSec: number; - }; - secrets?: Record; - }, + only = false, + }: TestTemplateOptionsX<"awaitEmailCompleteSec">, cleanup?: MetaTestCleanupFn, ) { Meta.test( { name: `Events and concurrent runs (${backendName})`, + only, }, async (t) => { Deno.env.set("SUB_BACKEND", backendName); @@ -354,17 +359,14 @@ export function retrySaveTestTemplate( { delays, secrets, - }: { - delays: { - awaitCompleteAll: number; - }; - secrets?: Record; - }, + only = false, + }: TestTemplateOptionsX<"awaitCompleteAll">, cleanup?: MetaTestCleanupFn, ) { Meta.test( { name: `Retry logic (${backendName})`, + only, }, async (t) => { Deno.env.set("SUB_BACKEND", backendName); @@ -524,17 +526,14 @@ export function childWorkflowTestTemplate( { delays, secrets, - }: { - delays: { - awaitCompleteSec: number; - }; - secrets?: Record; - }, + only = false, + }: TestTemplateOptionsX<"awaitCompleteSec">, cleanup?: MetaTestCleanupFn, ) { Meta.test( { name: `Child workflows (${backendName})`, + only, }, async (t) => { Deno.env.set("SUB_BACKEND", backendName); @@ -640,15 +639,15 @@ export function childWorkflowTestTemplate( `filter the runs given a nested expr (${backendName})`, async () => { await gql` - query { - search(name: "bumpPackage", filter: $filter) { - # started_at - # ended_at - status - value - } + query { + search(name: "bumpPackage", filter: $filter) { + # started_at + # ended_at + status + value } - ` + } + ` .withVars({ filter: { or: [ @@ -686,14 +685,14 @@ export function inputMutationTemplate( backendName: BackendName, { secrets, - }: { - secrets?: Record; - }, + only = false, + }: TestTemplateOptions, cleanup?: MetaTestCleanupFn, ) { Meta.test( { name: `kwargs input mutation after interrupts (${backendName})`, + only, }, async (t) => { Deno.env.set("SUB_BACKEND", backendName); diff --git a/tests/runtimes/temporal/__snapshots__/temporal_test.ts.snap b/tests/runtimes/temporal/__snapshots__/temporal_test.ts.snap index acbebcf37..11d763c39 100644 --- a/tests/runtimes/temporal/__snapshots__/temporal_test.ts.snap +++ b/tests/runtimes/temporal/__snapshots__/temporal_test.ts.snap @@ -63,11 +63,11 @@ snapshot[`Typegraph using temporal 1`] = ` }, { "type": "string", - "title": "root_start_fn_input_workflow_id_string" + "title": "string_e97ab" }, { "type": "list", - "title": "root_start_fn_input_args_root_start_fn_input_args_struct_list", + "title": "root_start_fn_input_args_struct_list_a2e91", "items": 5 }, { @@ -94,7 +94,7 @@ snapshot[`Typegraph using temporal 1`] = ` }, { "type": "object", - "title": "root_query_fn_input", + "title": "struct_ee884", "properties": { "workflow_id": 3, "run_id": 3, @@ -349,7 +349,7 @@ snapshot[`Typegraph using temporal 2`] = ` }, { "type": "string", - "title": "root_startKv_fn_input_workflow_id_string" + "title": "string_03667" }, { "type": "list", @@ -391,7 +391,7 @@ snapshot[`Typegraph using temporal 2`] = ` }, { "type": "list", - "title": "root_query_fn_input_args_root_startKv_fn_input_workflow_id_string_list", + "title": "root_query_fn_input_args_string_03667_list", "items": 3 }, { diff --git a/tests/runtimes/wasm_reflected/rust/Cargo.lock b/tests/runtimes/wasm_reflected/rust/Cargo.lock index d6b3aeef1..41f34e0ab 100644 --- a/tests/runtimes/wasm_reflected/rust/Cargo.lock +++ b/tests/runtimes/wasm_reflected/rust/Cargo.lock @@ -114,7 +114,7 @@ dependencies = [ [[package]] name = "rust" -version = "0.5.0-rc.7" +version = "0.5.0-rc.9" dependencies = [ "wit-bindgen", ] diff --git a/tests/runtimes/wasm_wire/rust/fdk.rs b/tests/runtimes/wasm_wire/rust/fdk.rs index 7cd94e752..b55796287 100644 --- a/tests/runtimes/wasm_wire/rust/fdk.rs +++ b/tests/runtimes/wasm_wire/rust/fdk.rs @@ -109,7 +109,7 @@ impl Router { } pub fn init(&self, args: InitArgs) -> Result { - 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())); } diff --git a/tests/sync/scripts/hello.ts b/tests/sync/scripts/hello.ts new file mode 100644 index 000000000..18fbbf9ab --- /dev/null +++ b/tests/sync/scripts/hello.ts @@ -0,0 +1,6 @@ +// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +// SPDX-License-Identifier: MPL-2.0 + +export function hello({ name }: { name: string }) { + return `Hello ${name}`; +} diff --git a/tests/sync/sync.py b/tests/sync/sync.py new file mode 100644 index 000000000..a5d5eb8bb --- /dev/null +++ b/tests/sync/sync.py @@ -0,0 +1,21 @@ +# Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +# SPDX-License-Identifier: MPL-2.0 + +from typegraph import t, typegraph, Policy, Graph +from typegraph.runtimes.deno import DenoRuntime + + +@typegraph() +def sync(g: Graph): + deno = DenoRuntime() + public = Policy.public() + + g.expose( + hello=deno.import_( + t.struct({"name": t.string()}), + t.string(), + name="hello", + module="scripts/hello.ts", + secrets=["ULTRA_SECRET"], + ).with_policy(public) + ) diff --git a/tests/sync/sync_config_test.ts b/tests/sync/sync_config_test.ts index 411654d31..029c823b6 100644 --- a/tests/sync/sync_config_test.ts +++ b/tests/sync/sync_config_test.ts @@ -42,6 +42,7 @@ Deno.test("test sync config", async (t) => { Deno.env.set("SYNC_S3_BUCKET", "bucket"); assertEquals(getSyncConfig(), { + forceRemove: false, redis: { hostname: "localhost", port: "6379", diff --git a/tests/sync/sync_force_remove_test.ts b/tests/sync/sync_force_remove_test.ts new file mode 100644 index 000000000..90126daf9 --- /dev/null +++ b/tests/sync/sync_force_remove_test.ts @@ -0,0 +1,114 @@ +// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +// SPDX-License-Identifier: MPL-2.0 + +import { gql, Meta } from "test-utils/mod.ts"; +import { connect } from "redis"; +import { S3Client } from "aws-sdk/client-s3"; +import { createBucket, listObjects, tryDeleteBucket } from "test-utils/s3.ts"; +import { assertEquals } from "@std/assert"; +import { clearSyncData, setupSync } from "test-utils/hooks.ts"; +import { Typegate } from "@metatype/typegate/typegate/mod.ts"; +import { + defaultTypegateConfigBase, + getTypegateConfig, + SyncConfig, +} from "@metatype/typegate/config.ts"; + +const redisKey = "typegraph"; +const redisEventKey = "typegraph_event"; + +async function cleanUp(config: typeof syncConfig) { + using redis = await connect(config.redis); + await redis.del(redisKey); + await redis.del(redisEventKey); + + const s3 = new S3Client(config.s3); + await tryDeleteBucket(s3, config.s3Bucket); + await createBucket(s3, config.s3Bucket); + s3.destroy(); + await redis.quit(); +} + +const syncConfig = { + redis: { + hostname: "localhost", + port: 6379, + password: "password", + db: 1, + }, + s3: { + endpoint: "http://localhost:9000", + region: "local", + credentials: { + accessKeyId: "minio", + secretAccessKey: "password", + }, + forcePathStyle: true, + }, + s3Bucket: "metatype-deno-runtime-sync-test", +}; + +async function spawnGate(syncConfig: SyncConfig) { + const config = getTypegateConfig({ + base: { + ...defaultTypegateConfigBase, + }, + }); + + return await Typegate.init({ + ...config, + sync: syncConfig, + }); +} + +Meta.test( + { + name: "Force cleanup at boot on sync mode", + syncConfig, + async setup() { + await clearSyncData(syncConfig); + await setupSync(syncConfig); + }, + async teardown() { + await cleanUp(syncConfig); + }, + }, + async (t) => { + await t.should( + "cleanup if forceRemove is true", + async () => { + const _engine = await t.engine("sync/sync.py", { + secrets: { + ULTRA_SECRET: + "if_you_can_read_me_on_an_ERROR_there_is_a_bug", + }, + }); + + const s3 = new S3Client(syncConfig.s3); + const initialObjects = await listObjects(s3, syncConfig.s3Bucket); + assertEquals(initialObjects?.length, 3); + + const gateNoRemove = await spawnGate(syncConfig); + const namesNoRemove = gateNoRemove.register.list().map(({ name }) => + name + ); + + const gateAfterRemove = await spawnGate({ + ...syncConfig, + forceRemove: true, + }); + const namesAfterRemove = gateAfterRemove.register.list().map(( + { name }, + ) => name); + + t.addCleanup(async () => { + await gateNoRemove[Symbol.asyncDispose](); + await gateAfterRemove[Symbol.asyncDispose](); + }); + + assertEquals(namesNoRemove, ["sync"]); + assertEquals(namesAfterRemove, []); // ! + }, + ); + }, +); diff --git a/tests/tools/schema_test.ts b/tests/tools/schema_test.ts new file mode 100644 index 000000000..715fec99b --- /dev/null +++ b/tests/tools/schema_test.ts @@ -0,0 +1,40 @@ +// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +// SPDX-License-Identifier: MPL-2.0 + +// NOTE: https://github.com/ajv-validator/ajv-formats/issues/85 +import Ajv from "https://esm.sh/ajv@8.12.0"; +import addFormats from "https://esm.sh/ajv-formats@3.0.1"; + +import { parse } from "npm:yaml"; +import schema from "@local/tools/schema/metatype.json" with { type: "json" }; +import * as path from "@std/path"; +import { assert } from "@std/assert"; +import { Meta } from "test-utils/mod.ts"; + +const files = [ + "../metatype.yml", + "../../examples/metatype.yaml", + "../../examples/templates/deno/metatype.yaml", + "../../examples/templates/node/metatype.yaml", + "../../examples/templates/python/metatype.yaml", + "../metagen/typegraphs/sample/metatype.yml", + "../metagen/typegraphs/identities/metatype.yml", +]; + +Meta.test("Configuration schema", () => { + const ajv = new Ajv(); + + addFormats(ajv); + + const validate = ajv.compile(schema); + const scriptDir = import.meta.dirname!; + + for (const file of files) { + const relativePath = path.resolve(scriptDir, file); + const yaml = Deno.readTextFileSync(relativePath); + const parsed = parse(yaml); + const result = validate(parsed); + + assert(result, `validation failed for '${file}'`); + } +}); diff --git a/tools/list-duplicates.ts b/tools/list-duplicates.ts index 7ec7c1aef..bd41a73c9 100755 --- a/tools/list-duplicates.ts +++ b/tools/list-duplicates.ts @@ -12,26 +12,303 @@ * Default: 0 */ -import { cyan, green, red, objectHash, parseArgs } from "./deps.ts"; +import { green, objectHash, parseArgs, red } from "./deps.ts"; // FIXME: import from @metatype/typegate import type { TypeGraphDS } from "../src/typegate/src/typegraph/mod.ts"; import { visitType } from "../src/typegate/src/typegraph/visitor.ts"; import { projectDir } from "./utils.ts"; import { TypeNode } from "../src/typegate/src/typegraph/type_node.ts"; +// Tries to detect structurally equivalent duplicates by iteratively +// updating composite types to refer to deduped types. +// I.e. optional and optional should be considered duplicates +// if A and B are duplicates of each other. +// This function is not perfect and is not able to detect some +// forms of structural equivalence. Additions to the TypeNode might +// break it. +export function listDuplicatesEnhanced(tg: TypeGraphDS, _rootIdx = 0) { + // to <- from + const reducedSetMap = new Map(); + const reducedSet = new Set(); + let cycleNo = 0; + const globalKindCounts = {} as Record; + const dupeKindCount = {} as Record; + while (true) { + cycleNo += 1; + const bins = new Map(); + const edges = new Map(); + const revEdges = new Map(); + + const addToRevEdges = (of: number, idx: number) => { + const prev = revEdges.get(of); + if (prev) { + prev.push(idx); + } else { + revEdges.set(of, [idx]); + } + }; + + let visitedTypesCount = 0; + { + let idx = -1; + for (const type of tg.types) { + idx += 1; + if (reducedSet.has(idx)) { + continue; + } + visitedTypesCount += 1; + const { title: _title, description: _description, ...structure } = type; + if (cycleNo == 1) { + incrementKindCount(type, globalKindCounts); + } + // deno-lint-ignore no-explicit-any + const hash = objectHash(structure as any); + { + const prev = bins.get(hash); + if (prev) { + prev.push([idx, type] as const); + } else { + bins.set(hash, [[idx, type]]); + } + } + switch (structure.type) { + case "function": + edges.set(idx, [structure.input, structure.output]); + addToRevEdges(structure.input, idx); + addToRevEdges(structure.output, idx); + break; + case "object": + edges.set(idx, Object.values(structure.properties)); + for (const dep of Object.values(structure.properties)) { + addToRevEdges(dep, idx); + } + break; + case "either": + edges.set(idx, structure.oneOf); + for (const dep of structure.oneOf) { + addToRevEdges(dep, idx); + } + break; + case "union": + edges.set(idx, structure.anyOf); + for (const dep of structure.anyOf) { + addToRevEdges(dep, idx); + } + break; + case "list": + edges.set(idx, [structure.items]); + addToRevEdges(structure.items, idx); + break; + case "optional": + edges.set(idx, [structure.item]); + addToRevEdges(structure.item, idx); + break; + case "boolean": + case "string": + case "file": + case "integer": + case "float": + // case "any": + break; + default: + throw new Error(`unsupported type: ${type.type}`); + } + } + } + + const dedupIndices = new Map(); + let cycleDupesFound = 0; + let cycleDupeBinsFound = 0; + for (const [_hash, bin] of bins.entries()) { + if (bin.length > 1) { + cycleDupeBinsFound += 1; + cycleDupesFound += bin.length; + const dedupedIdx = bin[0][0]; + const set = reducedSetMap.get(dedupedIdx) ?? []; + for (const [idx, _type] of bin.slice(1)) { + const removed = reducedSetMap.get(idx); + if (removed) { + reducedSetMap.delete(idx); + set.push(...removed); + } + set.push(idx); + reducedSet.add(idx); + dedupIndices.set(idx, dedupedIdx); + } + reducedSetMap.set(dedupedIdx, set); + } + } + if (cycleDupeBinsFound == 0) { + break; + } + console.log("reducing dupe bins", { + cycleDupesFound, + cycleDupeBinsFound, + reducedSetCount: reducedSet.size, + visitedTypesCount, + cycleNo, + }); + for (const [from, to] of dedupIndices) { + const itemsToUpdate = revEdges.get(from) ?? []; + for (const targetIdx of itemsToUpdate) { + const type = tg.types[targetIdx]; + const noMatchError = () => { + console.log("no match on dupe reduction", { + from: tg.types[from], + to: tg.types[to], + target: type, + }); + return Error("no match on dupe reduction"); + }; + switch (type.type) { + case "function": + if (type.input == from) { + type.input = to; + } else if (type.output == from) { + type.output = to; + } else { + throw noMatchError(); + } + break; + case "object": { + let updated = false; + for (const [key, id] of Object.entries(type.properties)) { + if (id == from) { + type.properties[key] = to; + updated = true; + break; + } + } + if (!updated) { + throw noMatchError(); + } + break; + } + case "either": { + const updateIdx = type.oneOf.indexOf(from); + if (updateIdx == -1) { + throw noMatchError(); + } + type.oneOf[updateIdx] = to; + break; + } + case "union": { + const updateIdx = type.anyOf.indexOf(from); + if (updateIdx == -1) { + throw noMatchError(); + } + type.anyOf[updateIdx] = to; + break; + } + case "list": + type.items = to; + break; + case "optional": + type.item = to; + break; + case "boolean": + case "string": + case "file": + case "integer": + case "float": + throw new Error("impossible"); + default: + throw new Error(`unsupported type: ${type.type}`); + } + } + } + } + const sortedDupes = [] as [number, number][]; + for (const [toIdx, bin] of reducedSetMap.entries()) { + if (bin.length > 1) { + sortedDupes.push([toIdx, bin.length]); + const toType = tg.types[toIdx]; + incrementKindCount(toType, dupeKindCount); + + // console.log(`${cyan(toType.title)}`); + for (const fromIdx of bin) { + const fromType = tg.types[fromIdx]; + incrementKindCount(fromType, dupeKindCount); + /* const injection = "injection" in fromType + // deno-lint-ignore no-explicit-any + ? ` (injection ${(fromType.injection as any).source})` + : ""; + console.log( + ` ${ + green(fromIdx.toString()) + } ${fromType.type}:${fromType.title}${injection}`, + ); */ + } + } + } + sortedDupes.sort((a, b) => a[1] - b[1]); + const dupesBinsFound = sortedDupes.length; + const dupesFound = sortedDupes + .map(([_rootIdx, count]) => count) + .reduce((a, b) => a + b); + console.log( + `${green("dupeCount")} kind:selected dedup root title`, + ); + for (const [rootIdx, count] of sortedDupes) { + const type = tg.types[rootIdx]; + console.log( + `${green(count.toString()) + "dupes"} ${type.type}:${type.title}`, + ); + } + console.log( + `${green("dupeCount")} kind:selected dedup root title`, + ); + console.log("that's it folks!", { + globalKindCounts, + dupeKindCount, + dupesFound, + dupesBinsFound, + totalCycles: cycleNo, + tgSize: tg.types.length, + }); +} + +function incrementKindCount(type: TypeNode, sumMap: Record) { + if (type.title.match(/_where_/)) { + const key = "prismaWhereFilterRelated"; + sumMap[key] = (sumMap[key] ?? 0) + 1; + } + if (type.title.match(/_update_input/)) { + const key = "prismaUpdateInputRelated"; + sumMap[key] = (sumMap[key] ?? 0) + 1; + } + if (type.title.match(/_create_input/)) { + const key = "prismaCreateInputRelated"; + sumMap[key] = (sumMap[key] ?? 0) + 1; + } + if (type.title.match(/_output/)) { + const key = "prismaOutputRelated"; + sumMap[key] = (sumMap[key] ?? 0) + 1; + } + sumMap[type.type] = (sumMap[type.type] ?? 0) + 1; +} + export function listDuplicates(tg: TypeGraphDS, rootIdx = 0) { const bins = new Map(); const duplicateNameBins = new Map(); visitType(tg, rootIdx, ({ type, idx }) => { const { title, description: _description, ...structure } = type; + // deno-lint-ignore no-explicit-any const hash = objectHash(structure as any); bins.set(hash, [...bins.get(hash) ?? [], [idx, type] as const]); - duplicateNameBins.set(title, [...duplicateNameBins.get(name) ?? [], [idx, type] as const]); + duplicateNameBins.set(title, [ + ...duplicateNameBins.get(title) ?? [], + [idx, type] as const, + ]); return true; }, { allowCircular: false }); - for (const [hash, bin] of bins.entries()) { + let dupesBinsFound = 0; + let dupesFound = 0; + for (const [_hash, bin] of bins.entries()) { if (bin.length > 1) { - console.log(`${cyan(hash)}`); + dupesBinsFound += 1; + dupesFound += bin.length; + /* console.log(`${cyan(hash)}`); for (const [idx, type] of bin) { const injection = "injection" in type ? ` (injection ${(type.injection as any).source})` @@ -40,13 +317,16 @@ export function listDuplicates(tg: TypeGraphDS, rootIdx = 0) { ` ${green(idx.toString())} ${type.type}:${type.title}${injection}`, ); } + */ } } + console.log({ dupesFound, dupesBinsFound }); for (const [hash, bin] of duplicateNameBins.entries()) { if (bin.length > 1) { console.log(`${red(hash)}`); for (const [idx, type] of bin) { const injection = "injection" in type + // deno-lint-ignore no-explicit-any ? ` (injection ${(type.injection as any).source})` : ""; console.log( @@ -93,7 +373,7 @@ const tgs: TypeGraphDS[] = JSON.parse( ); for (const tg of tgs) { - listDuplicates(tg, rootIdx); + listDuplicatesEnhanced(tg, rootIdx); } function argToInt(arg: string | undefined, defaultValue: number): number { diff --git a/tools/schema/metatype.json b/tools/schema/metatype.json new file mode 100644 index 000000000..1acfc02e1 --- /dev/null +++ b/tools/schema/metatype.json @@ -0,0 +1,296 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/metatype.json", + "title": "Metatype configuration file schema", + "additionalProperties": false, + "type": "object", + "definitions": { + "path": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "typegraphLoaderConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "$ref": "#/definitions/path", + "description": "A pattern or array of patterns to include." + }, + "exclude": { + "$ref": "#/definitions/path", + "description": "A pattern or array of patterns to exclude." + } + }, + "description": "Configuration for loading typegraph files." + }, + "generatorConfigBase": { + "type": "object", + "properties": { + "typegraph_name": { + "type": "string", + "description": "The name of the typegraph." + }, + "typegraph_path": { + "type": "string", + "description": "The target typegraph file path." + }, + "path": { + "type": "string", + "description": "The directory for the generated files." + }, + "template_dir": { + "type": "string", + "description": "The directory containing template files." + } + } + }, + "rustGeneratorConfigBase": { + "type": "object", + "properties": { + "skip_cargo_toml": { + "type": "boolean", + "description": "Whether to skip generating the `Cargo.toml` file." + }, + "skip_lib_rs": { + "type": "boolean", + "description": "Whether to skip generating the `lib.rs` file." + }, + "crate_name": { + "type": "string", + "description": "Generated crate name." + } + } + }, + "clientTsGeneratorConfig": { + "type": "object", + "properties": { + "generator": { + "const": "client_ts", + "description": "See: ." + } + } + }, + "clientPyGeneratorConfig": { + "type": "object", + "properties": { + "generator": { + "const": "client_py", + "description": "See: ." + } + } + }, + "clientRsGeneratorConfig": { + "allOf": [ + { + "type": "object", + "properties": { + "generator": { + "const": "client_rs", + "description": "See: ." + } + } + }, + { + "$ref": "#/definitions/rustGeneratorConfigBase" + } + ] + }, + "fdkTsGeneratorConfig": { + "type": "object", + "properties": { + "generator": { + "const": "fdk_typescript", + "description": "See: ." + }, + "stubbed_runtimes": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "fdkPyGeneratorConfig": { + "type": "object", + "properties": { + "generator": { + "const": "fdk_python", + "description": "See: ." + } + } + }, + "fdkRsGeneratorConfig": { + "allOf": [ + { + "type": "object", + "properties": { + "generator": { + "const": "fdk_rust", + "description": "See: ." + }, + "stubbed_runtimes": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + { + "$ref": "#/definitions/rustGeneratorConfigBase" + } + ] + }, + "fdkSubGeneratorConfig": { + "type": "object", + "properties": { + "generator": { + "const": "fdk_substantial" + } + } + }, + "generatorConfig": { + "allOf": [ + { + "$ref": "#/definitions/generatorConfigBase" + }, + { + "oneOf": [ + { + "$ref": "#/definitions/clientTsGeneratorConfig" + }, + { + "$ref": "#/definitions/clientPyGeneratorConfig" + }, + { + "$ref": "#/definitions/clientRsGeneratorConfig" + }, + { + "$ref": "#/definitions/fdkTsGeneratorConfig" + }, + { + "$ref": "#/definitions/fdkPyGeneratorConfig" + }, + { + "$ref": "#/definitions/fdkRsGeneratorConfig" + }, + { + "$ref": "#/definitions/fdkSubGeneratorConfig" + } + ] + } + ] + } + }, + "properties": { + "typegates": { + "type": "object", + "description": "Configuration for typegate nodes. See: .", + "additionalProperties": { + "type": "object", + "description": "Individual typegate node configuration.", + "additionalProperties": false, + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "The base URL of the typegate server, example: ." + }, + "prefix": { + "type": "string", + "description": "A prefix for typegraphs." + }, + "username": { + "type": "string", + "description": "Administrator username for the typegate." + }, + "password": { + "type": "string", + "description": "Administrator password for the typegate." + }, + "env": { + "type": "object", + "description": "Environment variables for the typegate.", + "additionalProperties": { + "type": "string" + } + }, + "secrets": { + "type": "object", + "description": "Secrets used for configuring runtimes within the typegraphs. See: .", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "typegraphs": { + "type": "object", + "additionalProperties": false, + "description": "Typegraph deployment configurations. See: .", + "properties": { + "python": { + "$ref": "#/definitions/typegraphLoaderConfig", + "description": "Configuration for Python typegraphs." + }, + "javascript": { + "$ref": "#/definitions/typegraphLoaderConfig", + "description": "Configuration for JavaScript typegraphs." + }, + "typescript": { + "$ref": "#/definitions/typegraphLoaderConfig", + "description": "Configuration for TypeScript typegraphs." + }, + "materializers": { + "type": "object", + "additionalProperties": false, + "description": "Materializer configurations for typegraphs.", + "properties": { + "prisma": { + "type": "object", + "additionalProperties": false, + "description": "Prisma materializer configuration.", + "properties": { + "migrations_path": { + "type": "string", + "description": "The directory for storing Prisma migration files." + } + } + } + } + } + } + }, + "metagen": { + "type": "object", + "additionalProperties": false, + "description": "Metagen configurations. See: .", + "properties": { + "targets": { + "type": "object", + "description": "Code generation target configurations.", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/definitions/generatorConfig" + } + } + } + } + } + } +}