From dc79efa5065b61e5d15e985aead3826858c1cd4c Mon Sep 17 00:00:00 2001 From: Yohe-Am <56622350+Yohe-Am@users.noreply.github.com> Date: Tue, 11 Jun 2024 15:23:27 +0300 Subject: [PATCH] docs: `/docs/reference/metagen` + `/docs/guides/wasm-functions` (#751) - Adds `/docs/reference/metagen` - Adds `/docs/guides/wasm-functions` - Adds a codegen section to `/docs/guides/external-functions` MDK-492. #### Migration notes ... - [ ] The change comes with new or modified tests - [ ] Hard-to-understand functions have explanatory comments - [x] End-user documentation is updated to reflect the change ## Summary by CodeRabbit - **New Features** - Added new targets for `metagen` with different generators and paths for TypeScript, Python, and Rust. - Introduced new functionality for defining and exposing typegraphs with policies in various environments (Deno, Python, Rust). - Added automated Rust WebAssembly project generation and compilation script. - Enhanced documentation with new sections and updated code examples using `TGExample`. - **Bug Fixes** - Updated `.gitignore` to exclude `*.wasm` files. - **Documentation** - Updated links and added detailed instructions for generating types using `metagen`. - **Refactor** - Switched from `HashMap` to `BTreeMap` and `HashSet` to `BTreeSet` in various modules for better data structure handling. - Added logging enhancements in the `Typegate` class. - **Chores** - Updated build script for Rust WebAssembly target. --- .gitignore | 1 + examples/metatype.yaml | 13 +- examples/typegraphs/metagen-deno.py | 34 + examples/typegraphs/metagen-deno.ts | 32 + examples/typegraphs/metagen-py.ts | 32 + examples/typegraphs/metagen-rs.py | 35 + examples/typegraphs/metagen-rs.ts | 34 + examples/typegraphs/metagen-sdk.py | 58 ++ examples/typegraphs/metagen-sdk.ts | 55 ++ examples/typegraphs/metagen/Cargo.lock | 314 ++++++ examples/typegraphs/metagen/Cargo.toml | 3 + examples/typegraphs/metagen/py/remix.py | 13 + examples/typegraphs/metagen/py/remix_types.py | 106 ++ examples/typegraphs/metagen/rs/Cargo.toml | 23 + examples/typegraphs/metagen/rs/build.sh | 20 + examples/typegraphs/metagen/rs/lib.rs | 28 + examples/typegraphs/metagen/rs/mdk.rs | 264 +++++ examples/typegraphs/metagen/ts/mdk.ts | 46 + examples/typegraphs/metagen/ts/remix.ts | 13 + libs/common/src/typegraph/mod.rs | 4 +- meta-cli/src/cli/gen.rs | 14 + meta-cli/src/logger.rs | 2 +- typegate/src/runtimes/python.ts | 3 + typegate/src/sync/replicated_map.ts | 8 +- typegate/src/typegate/mod.ts | 10 + typegate/tests/e2e/website/website_test.ts | 8 + .../__snapshots__/metagen_test.ts.snap | 928 +++++++++--------- typegate/tests/metagen/metagen_test.ts | 14 +- .../identities/py/handlers_types.py | 26 +- .../tests/runtimes/python/python_sync_test.ts | 83 +- .../tests/runtimes/wasm_wire/rust/build.sh | 4 +- typegraph/core/src/typegraph.rs | 6 + typegraph/core/src/utils/fs_host.rs | 4 +- .../core/src/utils/postprocess/python_rt.rs | 2 +- .../docs/concepts/features-overview/index.mdx | 6 +- .../docs/guides/external-functions/index.mdx | 100 +- .../guides/external-functions/metagen.graphql | 13 + .../programmatic-deployment/index.mdx | 0 website/docs/guides/wasm-functions/index.mdx | 148 +++ .../guides/wasm-functions/metagen.graphql | 13 + website/docs/reference/changelog.mdx | 2 +- website/docs/reference/ecosystem/index.mdx | 2 +- website/docs/reference/metagen/index.mdx | 220 +++++ website/packages/code-loader-transform.js | 2 +- .../projects/tab-first-project-python.mdx | 2 +- website/src/components/MiniQL/index.tsx | 21 +- whiz.yaml | 1 + 47 files changed, 2197 insertions(+), 573 deletions(-) create mode 100644 examples/typegraphs/metagen-deno.py create mode 100644 examples/typegraphs/metagen-deno.ts create mode 100644 examples/typegraphs/metagen-py.ts create mode 100644 examples/typegraphs/metagen-rs.py create mode 100644 examples/typegraphs/metagen-rs.ts create mode 100644 examples/typegraphs/metagen-sdk.py create mode 100644 examples/typegraphs/metagen-sdk.ts create mode 100644 examples/typegraphs/metagen/Cargo.lock create mode 100644 examples/typegraphs/metagen/Cargo.toml create mode 100644 examples/typegraphs/metagen/py/remix.py create mode 100644 examples/typegraphs/metagen/py/remix_types.py create mode 100644 examples/typegraphs/metagen/rs/Cargo.toml create mode 100644 examples/typegraphs/metagen/rs/build.sh create mode 100644 examples/typegraphs/metagen/rs/lib.rs create mode 100644 examples/typegraphs/metagen/rs/mdk.rs create mode 100644 examples/typegraphs/metagen/ts/mdk.ts create mode 100644 examples/typegraphs/metagen/ts/remix.ts create mode 100644 website/docs/guides/external-functions/metagen.graphql rename website/docs/{reference => guides}/programmatic-deployment/index.mdx (100%) create mode 100644 website/docs/guides/wasm-functions/index.mdx create mode 100644 website/docs/guides/wasm-functions/metagen.graphql create mode 100644 website/docs/reference/metagen/index.mdx diff --git a/.gitignore b/.gitignore index 3352e89768..5fdb271382 100644 --- a/.gitignore +++ b/.gitignore @@ -48,4 +48,5 @@ typegraph/python/typegraph/gen *.egg-info/ examples/typegraphs/migrations +examples/typegraphs/**/*.wasm libs/pyrt_wit_wire/wit_wire diff --git a/examples/metatype.yaml b/examples/metatype.yaml index ac0a186b0a..02aca2f543 100644 --- a/examples/metatype.yaml +++ b/examples/metatype.yaml @@ -144,4 +144,15 @@ typegraphs: metagen: targets: - main: + metagen_deno: + - generator: mdk_typescript + path: ./typegraphs/metagen/ts/ + typegraph_path: ./typegraphs/metagen-deno.ts + metagen_py: + - generator: mdk_python + path: ./typegraphs/metagen/py/ + typegraph_path: ./typegraphs/metagen-py.ts + metagen_rs: + - generator: mdk_rust + path: ./typegraphs/metagen/rs/ + typegraph_path: ./typegraphs/metagen-rs.ts diff --git a/examples/typegraphs/metagen-deno.py b/examples/typegraphs/metagen-deno.py new file mode 100644 index 0000000000..3d03217905 --- /dev/null +++ b/examples/typegraphs/metagen-deno.py @@ -0,0 +1,34 @@ +# skip:start +from typegraph import typegraph, Policy, t, Graph +from typegraph.graph.params import Cors +from typegraph.runtimes.deno import DenoRuntime +# skip:end + + +@typegraph( + # skip:start + cors=Cors(allow_origin=["https://metatype.dev", "http://localhost:3000"]), + # skip:end +) +def metagen_deno(g: Graph): + idv3 = t.struct( + { + "title": t.string(), + "artist": t.string(), + "releaseTime": t.datetime(), + "mp3Url": t.uri(), + # explicit type names help when generating code + } + ).rename("idv3") + deno = DenoRuntime() + + g.expose( + Policy.public(), + remix=deno.import_( + idv3, + idv3, + module="./metagen/ts/remix.ts", + deps=["./metagen/ts/mdk.ts"], + name="remix_track", + ).rename("remix_track"), # explicit names help + ) diff --git a/examples/typegraphs/metagen-deno.ts b/examples/typegraphs/metagen-deno.ts new file mode 100644 index 0000000000..77a1b18940 --- /dev/null +++ b/examples/typegraphs/metagen-deno.ts @@ -0,0 +1,32 @@ +// skip:start +import { Policy, t, typegraph } from "@typegraph/sdk/index.js"; +import { DenoRuntime } from "@typegraph/sdk/runtimes/deno.js"; +// skip:end + +await typegraph({ + name: "metagen-deno", + // skip:next-line + cors: { allowOrigin: ["https://metatype.dev", "http://localhost:3000"] }, +}, (g) => { + const idv3 = t.struct({ + title: t.string(), + artist: t.string(), + releaseTime: t.datetime(), + mp3Url: t.uri(), + // explicit type names help when generating code + }).rename("idv3"); + + const deno = new DenoRuntime(); + + g.expose({ + remix: deno.import( + idv3, + idv3, + { + module: "./metagen/ts/remix.ts", + deps: ["./metagen/ts/mdk.ts"], + name: "remix_track", + }, + ).rename("remix_track"), // explicit names help + }, Policy.public()); +}); diff --git a/examples/typegraphs/metagen-py.ts b/examples/typegraphs/metagen-py.ts new file mode 100644 index 0000000000..a3f546c1ce --- /dev/null +++ b/examples/typegraphs/metagen-py.ts @@ -0,0 +1,32 @@ +// skip:start +import { Policy, t, typegraph } from "@typegraph/sdk/index.js"; +import { PythonRuntime } from "@typegraph/sdk/runtimes/python.js"; +// skip:end + +await typegraph({ + name: "metagen-py", + // skip:next-line + cors: { allowOrigin: ["https://metatype.dev", "http://localhost:3000"] }, +}, (g) => { + const idv3 = t.struct({ + title: t.string(), + artist: t.string(), + releaseTime: t.datetime(), + mp3Url: t.uri(), + // explicit type names help when generating code + }).rename("idv3"); + + const python = new PythonRuntime(); + + g.expose({ + remix: python.import( + idv3, + idv3, + { + module: "./metagen/py/remix.py", + deps: ["./metagen/py/remix_types.py"], + name: "remix_track", + }, + ).rename("remix_track"), // explicit names help + }, Policy.public()); +}); diff --git a/examples/typegraphs/metagen-rs.py b/examples/typegraphs/metagen-rs.py new file mode 100644 index 0000000000..5e7fae8a6c --- /dev/null +++ b/examples/typegraphs/metagen-rs.py @@ -0,0 +1,35 @@ +# skip:start +from typegraph import typegraph, Policy, t, Graph +from typegraph.graph.params import Cors +from typegraph.runtimes.wasm import WasmRuntime +# skip:end + + +@typegraph( + # skip:start + cors=Cors(allow_origin=["https://metatype.dev", "http://localhost:3000"]), + # skip:end +) +def metagen_rs(g: Graph): + idv3 = t.struct( + { + "title": t.string(), + "artist": t.string(), + "releaseTime": t.datetime(), + "mp3Url": t.uri(), + # explicit type names help when generating code + } + ).rename("idv3") + + # the wire flavour is availible through a static + # constructor + wasm = WasmRuntime.wire("metagen/rust.wasm") + + g.expose( + Policy.public(), + remix=wasm.handler( + idv3, + idv3, + name="remix_track", + ).rename("remix_track"), # explicit names help + ) diff --git a/examples/typegraphs/metagen-rs.ts b/examples/typegraphs/metagen-rs.ts new file mode 100644 index 0000000000..26f8add0a8 --- /dev/null +++ b/examples/typegraphs/metagen-rs.ts @@ -0,0 +1,34 @@ +// skip:start +import { Policy, t, typegraph } from "@typegraph/sdk/index.js"; +import { WasmRuntime } from "@typegraph/sdk/runtimes/wasm.js"; +// skip:end + +await typegraph({ + name: "metagen-rs", + // skip:next-line + cors: { allowOrigin: ["https://metatype.dev", "http://localhost:3000"] }, +}, (g) => { + const idv3 = t.struct({ + title: t.string(), + artist: t.string(), + releaseTime: t.datetime(), + mp3Url: t.uri(), + // explicit type names help when generating code + }).rename("idv3"); + + // the wire flavour is availible through a static + // constructor + const wasm = WasmRuntime.wire("metagen/rust.wasm"); + + g.expose({ + remix: wasm.handler( + idv3, + idv3, + { + name: "remix_track", + }, + // the traits will map to the name of the materializer + // and also the the name of the handler mentioned above + ).rename("remix_track"), + }, Policy.public()); +}); diff --git a/examples/typegraphs/metagen-sdk.py b/examples/typegraphs/metagen-sdk.py new file mode 100644 index 0000000000..a9cddb8195 --- /dev/null +++ b/examples/typegraphs/metagen-sdk.py @@ -0,0 +1,58 @@ +# skip:start +from typegraph import typegraph, Policy, t, Graph +from typegraph.runtimes.deno import DenoRuntime +from typegraph.graph.params import Cors + +# skip:end +import os +from typegraph.graph.metagen import Metagen + + +@typegraph( + # skip:start + cors=Cors(allow_origin=["https://metatype.dev", "http://localhost:3000"]), + # skip:end +) +def metagen_sdk(g: Graph): + idv3 = t.struct( + { + "title": t.string(), + "artist": t.string(), + "releaseTime": t.datetime(), + "mp3Url": t.uri(), + } + ).rename("idv3") + deno = DenoRuntime() + + g.expose( + Policy.public(), + remix=deno.import_( + idv3, + idv3, + module="./metagen/ts/remix.ts", + deps=["./metagen/ts/mdk.ts"], + name="remix_track", + ).rename("remix_track"), + ) + + +if __name__ == "__main__" and False: + metagen = Metagen( + # the workspace root that our config is relative to + os.path.dirname(os.path.abspath(__file__)), + # the rest is pretty similar to the CLI config + { + "targets": { + "main": [ + { + "generator": "mdk_typescript", + "typegraph_path": __file__, + "path": "funcs/", + }, + ], + }, + }, + ) + tg = metagen_sdk() + # dry_run doesn't write to disk + items = metagen.dry_run(tg, "main", None) diff --git a/examples/typegraphs/metagen-sdk.ts b/examples/typegraphs/metagen-sdk.ts new file mode 100644 index 0000000000..d7664c472a --- /dev/null +++ b/examples/typegraphs/metagen-sdk.ts @@ -0,0 +1,55 @@ +// skip:start +import { Policy, t, typegraph } from "@typegraph/sdk/index.js"; +import { DenoRuntime } from "@typegraph/sdk/runtimes/deno.js"; +// skip:end +import { Metagen } from "@typegraph/sdk/metagen.js"; + +// get typegraph desc here +const tg = await typegraph({ + name: "metagen-sdk", + // skip:next-line + cors: { allowOrigin: ["https://metatype.dev", "http://localhost:3000"] }, +}, (g) => { + const idv3 = t.struct({ + title: t.string(), + artist: t.string(), + releaseTime: t.datetime(), + mp3Url: t.uri(), + }).rename("idv3"); + + const deno = new DenoRuntime(); + + g.expose({ + remix: deno.import( + idv3, + idv3, + { + module: "./metagen/ts/remix.ts", + deps: ["./metagen/ts/mdk.ts"], + name: "remix_track", + }, + ).rename("remix_track") + }, Policy.public()); +}); + +if (false) { + const myPath = import.meta.url.replace("file://", ""); + const metagen = new Metagen( + // the workspace root that our config is relative to + myPath + "/..", + // this rest of the config is similmilar to the CLI config + { + "targets": { + "main": [ + { + "generator": "mdk_typescript", + "typegraph_path": myPath, + "path": "funcs/", + }, + ], + }, + } + ); + // dry_run doesn't write to disk + metagen.dryRun(tg, "main") +} diff --git a/examples/typegraphs/metagen/Cargo.lock b/examples/typegraphs/metagen/Cargo.lock new file mode 100644 index 0000000000..2fe92e6192 --- /dev/null +++ b/examples/typegraphs/metagen/Cargo.lock @@ -0,0 +1,314 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "metagen_rs_mdk" +version = "0.0.1" +dependencies = [ + "anyhow", + "serde", + "serde_json", + "wit-bindgen", +] + +[[package]] +name = "proc-macro2" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "spdx" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47317bbaf63785b53861e1ae2d11b80d6b624211d42cb20efcd210ee6f8a14bc" +dependencies = [ + "smallvec", +] + +[[package]] +name = "syn" +version = "2.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "wasm-encoder" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-metadata" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fd83062c17b9f4985d438603cde0a5e8c5c8198201a6937f778b607924c7da2" +dependencies = [ + "anyhow", + "indexmap", + "serde", + "serde_derive", + "serde_json", + "spdx", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708" +dependencies = [ + "bitflags", + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "288f992ea30e6b5c531b52cdd5f3be81c148554b09ea416f058d16556ba92c27" +dependencies = [ + "bitflags", + "wit-bindgen-rt", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e85e72719ffbccf279359ad071497e47eb0675fe22106dea4ed2d8a7fcb60ba4" +dependencies = [ + "anyhow", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb8738270f32a2d6739973cbbb7c1b6dd8959ce515578a6e19165853272ee64" + +[[package]] +name = "wit-bindgen-rust" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a39a15d1ae2077688213611209849cad40e9e5cccf6e61951a425850677ff3" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d376d3ae5850526dfd00d937faea0d81a06fa18f7ac1e26f386d760f241a8f4b" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421c0c848a0660a8c22e2fd217929a0191f14476b68962afd2af89fd22e39825" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196d3ecfc4b759a8573bf86a9b3f8996b304b3732e4c7de81655f875f6efdca6" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] diff --git a/examples/typegraphs/metagen/Cargo.toml b/examples/typegraphs/metagen/Cargo.toml new file mode 100644 index 0000000000..0c138cb7f9 --- /dev/null +++ b/examples/typegraphs/metagen/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +resolver = "2" +members = ["rs/"] diff --git a/examples/typegraphs/metagen/py/remix.py b/examples/typegraphs/metagen/py/remix.py new file mode 100644 index 0000000000..5992ea744d --- /dev/null +++ b/examples/typegraphs/metagen/py/remix.py @@ -0,0 +1,13 @@ +from .remix_types import typed_remix_track, Idv3 + + +# the following decorator makes sure your function +# adheres to the function types from the typegraph +@typed_remix_track +def remix_track(inp: Idv3) -> Idv3: + return Idv3( + title=f"{inp.title} (Remix)", + artist=f"{inp.artist} + DJ Cloud", + releaseTime=inp.releaseTime, + mp3Url="https://mp3.url/remix1", + ) diff --git a/examples/typegraphs/metagen/py/remix_types.py b/examples/typegraphs/metagen/py/remix_types.py new file mode 100644 index 0000000000..83d667e0ef --- /dev/null +++ b/examples/typegraphs/metagen/py/remix_types.py @@ -0,0 +1,106 @@ +from types import NoneType +from typing import Callable, List, Union, get_origin, ForwardRef, Any +from dataclasses import dataclass, asdict, fields + +FORWARD_REFS = {} + + +class Struct: + def repr(self): + return asdict(self) + + @staticmethod + def try_new(dt_class, val: Any): + # Object + ftypes = {f.name: f.type for f in fields(dt_class)} + attrs = {} + for f in val: + fval = val[f] + ftype = ftypes[f] + serialized = False + # Union + if get_origin(ftype) is Union: + try: + attrs[f] = Struct.try_union(ftype.__args__, fval) + serialized = True + except Exception: + pass + # List + elif get_origin(ftype) is list: + try: + attrs[f] = Struct.try_typed_list(ftype.__args__, fval) + serialized = True + except Exception: + pass + # Any + if not serialized: + if isinstance(ftype, str) and ftype in FORWARD_REFS: + klass = FORWARD_REFS[ftype] + attrs[f] = Struct.new(klass, fval) + else: + attrs[f] = Struct.new(ftype, fval) + return dt_class(**attrs) + + @staticmethod + def try_typed_list(tpe: Any, items: Any): + hint = tpe.__args__[0] + klass = ( + FORWARD_REFS[hint.__forward_arg__] if isinstance(hint, ForwardRef) else hint + ) + return [Struct.new(klass, v) for v in items] + + @staticmethod + def try_union(variants: List[Any], val: Any): + errors = [] + for variant in variants: + try: + if variant is NoneType: + if val is None: + return None + else: + continue + if get_origin(variant) is list: + if isinstance(val, list): + return Struct.try_typed_list(variant, val) + else: + continue + klass = FORWARD_REFS[variant.__forward_arg__] + return Struct.try_new(klass, val) + except Exception as e: + errors.append(str(e)) + raise Exception("\n".join(errors)) + + @staticmethod + def new(dt_class: Any, val: Any): + try: + return Struct.try_new(dt_class, val) + except Exception: + return val + + +@dataclass +class Idv3(Struct): + title: str + artist: str + releaseTime: str + mp3Url: str + + +FORWARD_REFS["Idv3"] = Idv3 + + +def __repr(value: Any): + if isinstance(value, Struct): + return value.repr() + return value + + +def typed_remix_track(user_fn: Callable[[Idv3], Idv3]): + def exported_wrapper(raw_inp): + inp: Idv3 = Struct.new(Idv3, raw_inp) + out: Idv3 = user_fn(inp) + if isinstance(out, list): + return [__repr(v) for v in out] + return __repr(out) + + return exported_wrapper diff --git a/examples/typegraphs/metagen/rs/Cargo.toml b/examples/typegraphs/metagen/rs/Cargo.toml new file mode 100644 index 0000000000..8160994c53 --- /dev/null +++ b/examples/typegraphs/metagen/rs/Cargo.toml @@ -0,0 +1,23 @@ +package.name = "metagen_rs_mdk" +package.edition = "2021" +package.version = "0.0.1" + +# we need to use a specific library crate type to build +# wasm components in rust +[lib] +path = "lib.rs" +crate-type = ["cdylib", "rlib"] + +# the following dependencies are used by the generated code +[dependencies] +anyhow = "1" # error handling +serde = { version = "1", features = ["derive"] } # serialization +serde_json = "1" #json serialization +wit-bindgen = "0.22.0" # wasm component biding + +# we set the following flags to minimize code size +# when buliding in the release mode +# this keeps our wasm files small +[profile.release] +strip = "symbols" +opt-level = "z" diff --git a/examples/typegraphs/metagen/rs/build.sh b/examples/typegraphs/metagen/rs/build.sh new file mode 100644 index 0000000000..7d91b233d4 --- /dev/null +++ b/examples/typegraphs/metagen/rs/build.sh @@ -0,0 +1,20 @@ +set -eux + +cd ../../ +cargo run -p meta-cli -- -C metagen/ gen metagen_rs +cd - + +ADAPTOR="../../../../tmp/wasi_snapshot_preview1.reactor.wasm" +[ -f "$ADAPTOR" ] || ghjk x install-wasi-adapter + +TARGET=wasm32-wasi +CRATE_NAME=metagen_rs_mdk +cargo build -p $CRATE_NAME --target $TARGET # --release +# wasm-opt --enable-bulk-memory -Oz ./target/$TARGET/release/rust.wasm -o ./target/rust-component.wasm.opt +wasm-tools component new \ + ../target/$TARGET/debug/$CRATE_NAME.wasm \ + -o ../target/rust-component.wasm \ + --adapt wasi_snapshot_preview1=$ADAPTOR +wasm-tools component wit ../target/rust-component.wasm + +cp ../target/rust-component.wasm ../rust.wasm diff --git a/examples/typegraphs/metagen/rs/lib.rs b/examples/typegraphs/metagen/rs/lib.rs new file mode 100644 index 0000000000..f1b2178f6c --- /dev/null +++ b/examples/typegraphs/metagen/rs/lib.rs @@ -0,0 +1,28 @@ +mod mdk; +pub use mdk::*; + +// the macro sets up all the glue +init_mat! { + // the hook is expected to return a MatBuilder instance + hook: || { + // initialize global stuff here if you need it + MatBuilder::new() + // register function handlers here + // each trait will map to the name of the + // handler found in the typegraph + .register_handler(stubs::RemixTrack::erased(MyMat)) + } +} + +struct MyMat; + +impl stubs::RemixTrack for MyMat { + fn handle(&self, input: types::Idv3, _cx: Ctx) -> anyhow::Result { + Ok(types::Idv3 { + title: format!("{} (Remix)", input.title), + artist: format!("{} + DJ Cloud", input.artist), + release_time: input.release_time, + mp3_url: "https://mp3.url/shumba2".to_string(), + }) + } +} diff --git a/examples/typegraphs/metagen/rs/mdk.rs b/examples/typegraphs/metagen/rs/mdk.rs new file mode 100644 index 0000000000..16bc9ede66 --- /dev/null +++ b/examples/typegraphs/metagen/rs/mdk.rs @@ -0,0 +1,264 @@ +// This file was @generated by metagen and is intended +// to be generated again on subsequent metagen runs. +#![cfg_attr(rustfmt, rustfmt_skip)] + +// gen-static-start +#![allow(unused)] + +pub mod wit { + wit_bindgen::generate!({ + pub_export_macro: true, + + inline: "package metatype:wit-wire; + +interface typegate-wire { + hostcall: func(op-name: string, json: string) -> result; +} + +interface mat-wire { + type json-str = string; + + record mat-info { + op-name: string, + mat-title: string, + mat-hash: string, + mat-data-json: string, + } + + record init-args { + metatype-version: string, + expected-ops: list + } + + record init-response { + ok: bool + } + + variant init-error { + version-mismatch(string), + unexpected-mat(mat-info), + other(string) + } + + init: func(args: init-args) -> result; + + record handle-req { + op-name: string, + in-json: json-str, + } + + variant handle-err { + no-handler, + in-json-err(string), + handler-err(string), + } + + handle: func(req: handle-req) -> result; +} + +world wit-wire { + import typegate-wire; + + export mat-wire; +} +" + }); +} + +use std::cell::RefCell; +use std::collections::HashMap; + +use wit::exports::metatype::wit_wire::mat_wire::*; +use wit::metatype::wit_wire::typegate_wire::hostcall; + +pub type HandlerFn = Box Result>; + +pub struct ErasedHandler { + mat_id: String, + mat_trait: String, + mat_title: String, + handler_fn: HandlerFn, +} + +pub struct MatBuilder { + handlers: HashMap, +} + +impl MatBuilder { + pub fn new() -> Self { + Self { + handlers: Default::default(), + } + } + + pub fn register_handler(mut self, handler: ErasedHandler) -> Self { + self.handlers.insert(handler.mat_trait.clone(), handler); + self + } +} + +pub struct Router { + handlers: HashMap, +} + +impl Router { + pub fn from_builder(builder: MatBuilder) -> Self { + Self { + handlers: builder.handlers, + } + } + + pub fn init(&self, args: InitArgs) -> Result { + static MT_VERSION: &str = "0.4.3-0"; + if args.metatype_version != MT_VERSION { + return Err(InitError::VersionMismatch(MT_VERSION.into())); + } + for info in args.expected_ops { + let mat_trait = stubs::op_to_trait_name(&info.op_name); + if !self.handlers.contains_key(mat_trait) { + return Err(InitError::UnexpectedMat(info)); + } + } + Ok(InitResponse { ok: true }) + } + + pub fn handle(&self, req: HandleReq) -> Result { + let mat_trait = stubs::op_to_trait_name(&req.op_name); + let Some(handler) = self.handlers.get(mat_trait) else { + return Err(HandleErr::NoHandler); + }; + let cx = Ctx {}; + (handler.handler_fn)(&req.in_json, cx) + } +} + +pub type InitCallback = fn() -> anyhow::Result; + +thread_local! { + pub static MAT_STATE: RefCell = panic!("MAT_STATE has not been initialized"); +} + +pub struct Ctx {} + +impl Ctx { + pub fn gql( + &self, + query: &str, + variables: impl Into, + ) -> Result + where + O: serde::de::DeserializeOwned, + { + match hostcall( + "gql", + &serde_json::to_string(&serde_json::json!({ + "query": query, + "variables": variables.into(), + }))?, + ) { + Ok(json) => Ok(serde_json::from_str(&json[..])?), + Err(json) => Err(GraphqlRunError::HostError(serde_json::from_str(&json)?)), + } + } +} + +#[derive(Debug)] +pub enum GraphqlRunError { + JsonError(serde_json::Error), + HostError(serde_json::Value), +} + +impl std::error::Error for GraphqlRunError {} + +impl From for GraphqlRunError { + fn from(value: serde_json::Error) -> Self { + Self::JsonError(value) + } +} + +impl std::fmt::Display for GraphqlRunError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + GraphqlRunError::JsonError(msg) => write!(f, "json error: {msg}"), + GraphqlRunError::HostError(serde_json::Value::Object(map)) + if map.contains_key("message") => + { + write!(f, "host error: {}", map["message"]) + } + GraphqlRunError::HostError(val) => write!(f, "host error: {val:?}"), + } + } +} + +#[macro_export] +macro_rules! init_mat { + (hook: $init_hook:expr) => { + struct MatWireGuest; + use wit::exports::metatype::wit_wire::mat_wire::*; + wit::export!(MatWireGuest with_types_in wit); + + #[allow(unused)] + impl Guest for MatWireGuest { + fn handle(req: HandleReq) -> Result { + MAT_STATE.with(|router| { + let router = router.borrow(); + router.handle(req) + }) + } + + fn init(args: InitArgs) -> Result { + let hook = $init_hook; + let router = Router::from_builder(hook()); + let resp = router.init(args)?; + MAT_STATE.set(router); + Ok(resp) + } + } + }; +} +// gen-static-end +use types::*; +pub mod types { + use super::*; + pub type StringDateTime = String; + pub type StringUri = String; + #[derive(Debug, serde::Serialize, serde::Deserialize)] + pub struct Idv3 { + pub title: String, + pub artist: String, + #[serde(rename = "releaseTime")] + pub release_time: StringDateTime, + #[serde(rename = "mp3Url")] + pub mp3_url: StringUri, + } +} +use stubs::*; +pub mod stubs { + use super::*; + pub trait RemixTrack: Sized + 'static { + fn erased(self) -> ErasedHandler { + ErasedHandler { + mat_id: "remix_track".into(), + mat_title: "remix_track".into(), + mat_trait: "RemixTrack".into(), + handler_fn: Box::new(move |req, cx| { + let req = serde_json::from_str(req) + .map_err(|err| HandleErr::InJsonErr(format!("{err}")))?; + let res = self + .handle(req, cx) + .map_err(|err| HandleErr::HandlerErr(format!("{err}")))?; + serde_json::to_string(&res) + .map_err(|err| HandleErr::HandlerErr(format!("{err}"))) + }), + } + } + + fn handle(&self, input: Idv3, cx: Ctx) -> anyhow::Result; + } + pub fn op_to_trait_name(op_name: &str) -> &'static str { + match op_name { + "remix_track" => "RemixTrack", + _ => panic!("unrecognized op_name: {op_name}"), + } + } +} diff --git a/examples/typegraphs/metagen/ts/mdk.ts b/examples/typegraphs/metagen/ts/mdk.ts new file mode 100644 index 0000000000..b34231aaa1 --- /dev/null +++ b/examples/typegraphs/metagen/ts/mdk.ts @@ -0,0 +1,46 @@ +// This file was @generated by metagen and is intended +// to be generated again on subsequent metagen runs. + +export type Ctx = { + parent?: Record; + /** + * Request context extracted by auth extractors. + */ + context?: Record; + secrets: Record; + effect: "create" | "update" | "delete" | "read" | undefined | null; + meta: { + url: string; + token: string; + }; + headers: Record; +}; + +/** + * Access features on your typegraph deployment. + */ +export type Deployment = { + gql: (query: readonly string[], ...args: unknown[]) => { + run: ( + variables: Record, + ) => Promise>; + }; +}; + +export type Handler = ( + input: In, + ctx: Ctx, + tg: Deployment, +) => Out | Promise; + +export type StringDateTime = string; +export type StringUri = string; +export type Idv3 = { + title: string; + artist: string; + releaseTime: StringDateTime; + mp3Url: StringUri; +}; + + +export type RemixTrackHandler = Handler; diff --git a/examples/typegraphs/metagen/ts/remix.ts b/examples/typegraphs/metagen/ts/remix.ts new file mode 100644 index 0000000000..8c51abe076 --- /dev/null +++ b/examples/typegraphs/metagen/ts/remix.ts @@ -0,0 +1,13 @@ +import type { RemixTrackHandler, Ctx, Idv3 } from "./mdk.ts" + +// the name of the export must match the one referred int he typegraph +export const remix_track: RemixTrackHandler = (inp, cx: Ctx) => { + const out: Idv3 = { + title: `${inp.title} (Remix)`, + artist: `${inp.artist} + DJ Cloud`, + releaseTime: new Date().toISOString(), + // S3Runtime could be used to really provide this service + mp3Url: `${cx.meta.url}/get_mp3` + }; + return out; +} diff --git a/libs/common/src/typegraph/mod.rs b/libs/common/src/typegraph/mod.rs index c96de7b900..c59254441b 100644 --- a/libs/common/src/typegraph/mod.rs +++ b/libs/common/src/typegraph/mod.rs @@ -10,7 +10,7 @@ pub mod visitor; pub use types::*; -use std::collections::HashMap; +use std::collections::BTreeMap; use std::hash::Hash; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -92,7 +92,7 @@ pub struct TypeMeta { pub rate: Option, pub version: String, pub random_seed: Option, - pub artifacts: HashMap, + pub artifacts: BTreeMap, } #[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] diff --git a/meta-cli/src/cli/gen.rs b/meta-cli/src/cli/gen.rs index db5feb1f06..3a14324541 100644 --- a/meta-cli/src/cli/gen.rs +++ b/meta-cli/src/cli/gen.rs @@ -60,6 +60,20 @@ impl Action for Gen { typegate: Arc::new(node), }; + if !mgen_conf.targets.contains_key(&self.gen_target) { + error!("no metagen target found under key {:?}", self.gen_target); + info!( + "availaible keys are:\n - {:?}", + mgen_conf + .targets + .keys() + .map(|str| &str[..]) + .collect::>() + .join("\n - ") + ); + bail!("no metagen target found under key {:?}", self.gen_target); + } + let files = metagen::generate_target( mgen_conf, &self.gen_target, diff --git a/meta-cli/src/logger.rs b/meta-cli/src/logger.rs index 5bf88b9390..e2cbfd156a 100644 --- a/meta-cli/src/logger.rs +++ b/meta-cli/src/logger.rs @@ -48,7 +48,7 @@ Args: {args:?} if std::env::var("RUST_LOG").is_err() { std::env::set_var("RUST_LOG", "info,actix_server=warn"); } - #[cfg(not(debug_assertions))] + // #[cfg(not(debug_assertions))] if std::env::var("RUST_SPANTRACE").is_err() { std::env::set_var("RUST_SPANTRACE", "0"); } diff --git a/typegate/src/runtimes/python.ts b/typegate/src/runtimes/python.ts index d8d907ba61..8bb3ce1cd1 100644 --- a/typegate/src/runtimes/python.ts +++ b/typegate/src/runtimes/python.ts @@ -148,6 +148,9 @@ export class PythonRuntime extends Runtime { } async deinit(): Promise { + // if (Deno.env.get("KILL_PY")) { + // throw new Error("wtf"); + // } this.logger.info("deinitializing PythonRuntime"); await using _drop = this.wire; } diff --git a/typegate/src/sync/replicated_map.ts b/typegate/src/sync/replicated_map.ts index c51c5dd794..a1f4303ac8 100644 --- a/typegate/src/sync/replicated_map.ts +++ b/typegate/src/sync/replicated_map.ts @@ -158,10 +158,10 @@ export class RedisReplicatedMap implements AsyncDisposable { if (!payload) { throw new ReplicatedMapError(`added message without payload ${name}`); } - logger.info(`received addition`, { name }); + logger.info(`received addition {}`, { name }); await this.memorySet(name, await deserializer(payload, false)); } else if (event === RM) { - logger.info(`received removal`, { name }); + logger.info(`received removal {}`, { name }); await this.memorySet(name, null); } else { throw new ReplicatedMapError( @@ -217,7 +217,7 @@ export class RedisReplicatedMap implements AsyncDisposable { const { key, ekey, serializer, redis } = this; await this.memorySet(name, elem); - logger.info(`sent addition`, { name, key, ekey, elem }); + logger.info(`sent addition {}`, { name, key, ekey, elem: !!elem }); await redis.eval( addCmd, @@ -238,7 +238,7 @@ export class RedisReplicatedMap implements AsyncDisposable { const { key, ekey, redis } = this; await this.memorySet(name, null); - logger.info(`sent removal`, { name, key }); + logger.info(`sent removal {}`, { name, key }); await redis.eval( rmCmd, diff --git a/typegate/src/typegate/mod.ts b/typegate/src/typegate/mod.ts index 7254f0f33b..fa137e9978 100644 --- a/typegate/src/typegate/mod.ts +++ b/typegate/src/typegate/mod.ts @@ -225,11 +225,21 @@ export class Typegate implements AsyncDisposable { } if (!engineName || ignoreList.has(engineName)) { + logger.error("engine not found on request url {}", { + engineName, + ignored: ignoreList.has(engineName), + url: request.url, + }); return notFound(); } const engine = this.register.get(engineName); if (!engine) { + logger.error("engine not found for request {}", { + engineName, + url: request.url, + engines: this.register.list().map((en) => en.name), + }); return notFound(); } diff --git a/typegate/tests/e2e/website/website_test.ts b/typegate/tests/e2e/website/website_test.ts index 4f6e0555a9..af3fad6efd 100644 --- a/typegate/tests/e2e/website/website_test.ts +++ b/typegate/tests/e2e/website/website_test.ts @@ -103,5 +103,13 @@ async function testSerializeAllPairs(t: MetaTest, dirPath: string) { } Meta.test("typegraphs comparison", async (t) => { + await t.should("build wasm artifacts", async () => { + assertEquals( + (await t.shell("bash build.sh".split(" "), { + currentDir: "examples/typegraphs/metagen/rs", + })).code, + 0, + ); + }); await testSerializeAllPairs(t, "examples/typegraphs"); }); diff --git a/typegate/tests/metagen/__snapshots__/metagen_test.ts.snap b/typegate/tests/metagen/__snapshots__/metagen_test.ts.snap index d07c7feef4..899a3af2ae 100644 --- a/typegate/tests/metagen/__snapshots__/metagen_test.ts.snap +++ b/typegate/tests/metagen/__snapshots__/metagen_test.ts.snap @@ -2,69 +2,41 @@ export const snapshot = {}; snapshot[`Metagen within sdk 1`] = ` [ - { - content: 'package.name = "example_metagen_mdk" -package.edition = "2021" -package.version = "0.0.1" - -[lib] -path = "lib.rs" -crate-type = ["cdylib", "rlib"] - -[dependencies] -anyhow = "1" -serde = { version = "1", features = ["derive"] } -serde_json = "1" -wit-bindgen = "0.22.0" - - -[profile.release] -strip = "symbols" -opt-level = "z"', - overwrite: false, - path: "./workspace/some/base/path/rust/Cargo.toml", - }, - { - content: '# - NOTE: only modules that are imported relatively -# 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 Student, Object7, typed_three - - -@typed_three -def three(inp: Object7) -> Student: - # TODO: write your logic here - raise Exception("three not implemented") - -', - overwrite: false, - path: "./workspace/some/base/path/python/other.py", - }, - { - content: '# - NOTE: only modules that are imported relatively -# 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 typed_fnTwo, Type8Student, TwoInput, typed_fnOne, TypeString6, Object7 - + [ + "0", + { + content: " +mod mdk; +pub use mdk::*; -@typed_fnTwo -def fnTwo(inp: TwoInput) -> TypeString6: - # TODO: write your logic here - raise Exception("fnTwo not implemented") +/* +init_mat! { + hook: || { + // initialize global stuff here if you need it + MatBuilder::new() + // register function handlers here + .register_handler(stubs::MyFunc::erased(MyMat)) + } +} -@typed_fnOne -def fnOne(inp: Object7) -> Type8Student: - # TODO: write your logic here - raise Exception("fnOne not implemented") +struct MyMat; -', - overwrite: false, - path: "./workspace/some/base/path/python/same_hit.py", - }, - { - content: 'from types import NoneType +// FIXME: use actual types from your mdk here +impl stubs::MyFunc for MyMat { + fn handle(&self, input: types::MyFuncIn, _cx: Ctx) -> anyhow::Result { + unimplemented!() + } +} +*/ +", + overwrite: false, + path: "./workspace/some/base/path/rust/lib.rs", + }, + ], + [ + "1", + { + content: 'from types import NoneType from typing import Callable, List, Union, get_origin, ForwardRef, Any from dataclasses import dataclass, asdict, fields @@ -165,8 +137,8 @@ class TwoInput(Struct): FORWARD_REFS["TwoInput"] = TwoInput -TypeString6 = str Type8Student = List["Student"] +TypeString6 = str def __repr(value: Any): @@ -175,6 +147,16 @@ def __repr(value: Any): return value +def typed_fnOne(user_fn: Callable[[Object7], Type8Student]): + def exported_wrapper(raw_inp): + inp: Object7 = Struct.new(Object7, raw_inp) + out: Type8Student = user_fn(inp) + if isinstance(out, list): + return [__repr(v) for v in out] + return __repr(out) + return exported_wrapper + + def typed_fnTwo(user_fn: Callable[[TwoInput], TypeString6]): def exported_wrapper(raw_inp): inp: TwoInput = Struct.new(TwoInput, raw_inp) @@ -185,10 +167,182 @@ def typed_fnTwo(user_fn: Callable[[TwoInput], TypeString6]): return exported_wrapper -def typed_fnOne(user_fn: Callable[[Object7], Type8Student]): +', + overwrite: true, + path: "./workspace/some/base/path/python/same_hit_types.py", + }, + ], + [ + "2", + { + content: '// This file was @generated by metagen and is intended +// to be generated again on subsequent metagen runs. + +export type Ctx = { + parent?: Record; + /** + * Request context extracted by auth extractors. + */ + context?: Record; + secrets: Record; + effect: "create" | "update" | "delete" | "read" | undefined | null; + meta: { + url: string; + token: string; + }; + headers: Record; +}; + +/** + * Access features on your typegraph deployment. + */ +export type Deployment = { + gql: (query: readonly string[], ...args: unknown[]) => { + run: ( + variables: Record, + ) => Promise>; + }; +}; + +export type Handler = ( + input: In, + ctx: Ctx, + tg: Deployment, +) => Out | Promise; + +export type Object7 = { + name: string; +}; +export type Student3 = Array; +export type Student43 = Student3 | null | undefined; +export type Student = { + id: number; + name: string; + peers: Student43; +}; +export type Student8 = Array; +export type TwoInput = { + name: string; +}; + + +export type Func18Handler = Handler; +export type Func19Handler = Handler; +export type Func20Handler = Handler; +', + overwrite: true, + path: "./workspace/some/base/path/ts/mdk.ts", + }, + ], + [ + "3", + { + content: 'from types import NoneType +from typing import Callable, List, Union, get_origin, ForwardRef, Any +from dataclasses import dataclass, asdict, fields + +FORWARD_REFS = {} + +class Struct(): + def repr(self): + return asdict(self) + + @staticmethod + def try_new(dt_class, val: Any): + # Object + ftypes = {f.name: f.type for f in fields(dt_class)} + attrs = {} + for f in val: + fval = val[f] + ftype = ftypes[f] + serialized = False + # Union + if get_origin(ftype) is Union: + try: + attrs[f] = Struct.try_union(ftype.__args__, fval) + serialized = True + except Exception: + pass + # List + elif get_origin(ftype) is list: + try: + attrs[f] = Struct.try_typed_list(ftype.__args__, fval) + serialized = True + except Exception: + pass + # Any + if not serialized: + if isinstance(ftype, str) and ftype in FORWARD_REFS: + klass = FORWARD_REFS[ftype] + attrs[f] = Struct.new(klass, fval) + else: + attrs[f] = Struct.new(ftype, fval) + return dt_class(**attrs) + + @staticmethod + def try_typed_list(tpe: Any, items: Any): + hint = tpe.__args__[0] + klass = ( + FORWARD_REFS[hint.__forward_arg__] if isinstance(hint, ForwardRef) else hint + ) + return [Struct.new(klass, v) for v in items] + + @staticmethod + def try_union(variants: List[Any], val: Any): + errors = [] + for variant in variants: + try: + if variant is NoneType: + if val is None: + return None + else: + continue + if get_origin(variant) is list: + if isinstance(val, list): + return Struct.try_typed_list(variant, val) + else: + continue + klass = FORWARD_REFS[variant.__forward_arg__] + return Struct.try_new(klass, val) + except Exception as e: + errors.append(str(e)) + raise Exception("\\\\n".join(errors)) + + @staticmethod + def new(dt_class: Any, val: Any): + try: + return Struct.try_new(dt_class, val) + except Exception: + return val + +@dataclass +class Object7(Struct): + name: str + + + +FORWARD_REFS["Object7"] = Object7 +@dataclass +class Student(Struct): + id: int + name: str + peers: Union[List["Student"], None] + + + +FORWARD_REFS["Student"] = Student + + +def __repr(value: Any): + if isinstance(value, Struct): + return value.repr() + return value + + +def typed_three(user_fn: Callable[[Object7], Student]): def exported_wrapper(raw_inp): inp: Object7 = Struct.new(Object7, raw_inp) - out: Type8Student = user_fn(inp) + out: Student = user_fn(inp) if isinstance(out, list): return [__repr(v) for v in out] return __repr(out) @@ -196,39 +350,59 @@ def typed_fnOne(user_fn: Callable[[Object7], Type8Student]): ', - overwrite: true, - path: "./workspace/some/base/path/python/same_hit_types.py", - }, - { - content: " -mod mdk; -pub use mdk::*; + overwrite: true, + path: "./workspace/some/base/path/python/other_types.py", + }, + ], + [ + "4", + { + content: '# - NOTE: only modules that are imported relatively +# 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 typed_three, Object7, Student -/* -init_mat! { - hook: || { - // initialize global stuff here if you need it - MatBuilder::new() - // register function handlers here - .register_handler(stubs::MyFunc::erased(MyMat)) - } -} -struct MyMat; +@typed_three +def three(inp: Object7) -> Student: + # TODO: write your logic here + raise Exception("three not implemented") -// FIXME: use actual types from your mdk here -impl stubs::MyFunc for MyMat { - fn handle(&self, input: types::MyFuncIn, _cx: Ctx) -> anyhow::Result { - unimplemented!() - } -} -*/ -", - overwrite: false, - path: "./workspace/some/base/path/rust/lib.rs", - }, - { - content: \`// This file was @generated by metagen and is intended +', + overwrite: false, + path: "./workspace/some/base/path/python/other.py", + }, + ], + [ + "5", + { + content: 'package.name = "example_metagen_mdk" +package.edition = "2021" +package.version = "0.0.1" + +[lib] +path = "lib.rs" +crate-type = ["cdylib", "rlib"] + +[dependencies] +anyhow = "1" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +wit-bindgen = "0.22.0" + + +[profile.release] +strip = "symbols" +opt-level = "z"', + overwrite: false, + path: "./workspace/some/base/path/rust/Cargo.toml", + }, + ], + [ + "6", + { + content: \`// This file was @generated by metagen and is intended // to be generated again on subsequent metagen runs. #![cfg_attr(rustfmt, rustfmt_skip)] @@ -538,70 +712,75 @@ pub mod stubs { } } \`, - overwrite: true, - path: "./workspace/some/base/path/rust/mdk.rs", - }, - { - content: '// This file was @generated by metagen and is intended -// to be generated again on subsequent metagen runs. + overwrite: true, + path: "./workspace/some/base/path/rust/mdk.rs", + }, + ], + [ + "7", + { + content: '# - NOTE: only modules that are imported relatively +# 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 Type8Student, Object7, typed_fnOne, TwoInput, TypeString6, typed_fnTwo -export type Ctx = { - parent?: Record; - /** - * Request context extracted by auth extractors. - */ - context?: Record; - secrets: Record; - effect: "create" | "update" | "delete" | "read" | undefined | null; - meta: { - url: string; - token: string; - }; - headers: Record; -}; -/** - * Access features on your typegraph deployment. - */ -export type Deployment = { - gql: (query: readonly string[], ...args: unknown[]) => { - run: ( - variables: Record, - ) => Promise>; - }; -}; +@typed_fnOne +def fnOne(inp: Object7) -> Type8Student: + # TODO: write your logic here + raise Exception("fnOne not implemented") -export type Handler = ( - input: In, - ctx: Ctx, - tg: Deployment, -) => Out | Promise; +@typed_fnTwo +def fnTwo(inp: TwoInput) -> TypeString6: + # TODO: write your logic here + raise Exception("fnTwo not implemented") -export type Object7 = { - name: string; -}; -export type Student3 = Array; -export type Student43 = Student3 | null | undefined; -export type Student = { - id: number; - name: string; - peers: Student43; -}; -export type Student8 = Array; -export type TwoInput = { - name: string; -}; +', + overwrite: false, + path: "./workspace/some/base/path/python/same_hit.py", + }, + ], +] +`; +snapshot[`Metagen within sdk 2`] = ` +[ + [ + "0", + { + content: " +mod mdk; +pub use mdk::*; -export type Func18Handler = Handler; -export type Func19Handler = Handler; -export type Func20Handler = Handler; -', - overwrite: true, - path: "./workspace/some/base/path/ts/mdk.ts", - }, - { - content: 'from types import NoneType +/* +init_mat! { + hook: || { + // initialize global stuff here if you need it + MatBuilder::new() + // register function handlers here + .register_handler(stubs::MyFunc::erased(MyMat)) + } +} + +struct MyMat; + +// FIXME: use actual types from your mdk here +impl stubs::MyFunc for MyMat { + fn handle(&self, input: types::MyFuncIn, _cx: Ctx) -> anyhow::Result { + unimplemented!() + } +} +*/ +", + overwrite: false, + path: "./workspace/some/base/path/rust/lib.rs", + }, + ], + [ + "1", + { + content: 'from types import NoneType from typing import Callable, List, Union, get_origin, ForwardRef, Any from dataclasses import dataclass, asdict, fields @@ -695,6 +874,15 @@ class Student(Struct): FORWARD_REFS["Student"] = Student +@dataclass +class TwoInput(Struct): + name: str + + + +FORWARD_REFS["TwoInput"] = TwoInput +Type8Student = List["Student"] +TypeString6 = str def __repr(value: Any): @@ -703,88 +891,97 @@ def __repr(value: Any): return value -def typed_three(user_fn: Callable[[Object7], Student]): +def typed_fnOne(user_fn: Callable[[Object7], Type8Student]): def exported_wrapper(raw_inp): inp: Object7 = Struct.new(Object7, raw_inp) - out: Student = user_fn(inp) + out: Type8Student = user_fn(inp) if isinstance(out, list): return [__repr(v) for v in out] return __repr(out) return exported_wrapper -', - overwrite: true, - path: "./workspace/some/base/path/python/other_types.py", - }, -] -`; - -snapshot[`Metagen within sdk 2`] = ` -[ - { - content: 'package.name = "example_metagen_mdk" -package.edition = "2021" -package.version = "0.0.1" - -[lib] -path = "lib.rs" -crate-type = ["cdylib", "rlib"] - -[dependencies] -anyhow = "1" -serde = { version = "1", features = ["derive"] } -serde_json = "1" -wit-bindgen = "0.22.0" - +def typed_fnTwo(user_fn: Callable[[TwoInput], TypeString6]): + def exported_wrapper(raw_inp): + inp: TwoInput = Struct.new(TwoInput, raw_inp) + out: TypeString6 = user_fn(inp) + if isinstance(out, list): + return [__repr(v) for v in out] + return __repr(out) + return exported_wrapper -[profile.release] -strip = "symbols" -opt-level = "z"', - overwrite: false, - path: "./workspace/some/base/path/rust/Cargo.toml", - }, - { - content: '# - NOTE: only modules that are imported relatively -# 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 Student, Object7, typed_three +', + overwrite: true, + path: "./workspace/some/base/path/python/same_hit_types.py", + }, + ], + [ + "2", + { + content: '// This file was @generated by metagen and is intended +// to be generated again on subsequent metagen runs. -@typed_three -def three(inp: Object7) -> Student: - # TODO: write your logic here - raise Exception("three not implemented") +export type Ctx = { + parent?: Record; + /** + * Request context extracted by auth extractors. + */ + context?: Record; + secrets: Record; + effect: "create" | "update" | "delete" | "read" | undefined | null; + meta: { + url: string; + token: string; + }; + headers: Record; +}; -', - overwrite: false, - path: "./workspace/some/base/path/python/other.py", - }, - { - content: '# - NOTE: only modules that are imported relatively -# 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 typed_fnTwo, Type8Student, TwoInput, typed_fnOne, TypeString6, Object7 +/** + * Access features on your typegraph deployment. + */ +export type Deployment = { + gql: (query: readonly string[], ...args: unknown[]) => { + run: ( + variables: Record, + ) => Promise>; + }; +}; +export type Handler = ( + input: In, + ctx: Ctx, + tg: Deployment, +) => Out | Promise; -@typed_fnTwo -def fnTwo(inp: TwoInput) -> TypeString6: - # TODO: write your logic here - raise Exception("fnTwo not implemented") +export type Object7 = { + name: string; +}; +export type Student3 = Array; +export type Student43 = Student3 | null | undefined; +export type Student = { + id: number; + name: string; + peers: Student43; +}; +export type Student8 = Array; +export type TwoInput = { + name: string; +}; -@typed_fnOne -def fnOne(inp: Object7) -> Type8Student: - # TODO: write your logic here - raise Exception("fnOne not implemented") +export type Func18Handler = Handler; +export type Func19Handler = Handler; +export type Func20Handler = Handler; ', - overwrite: false, - path: "./workspace/some/base/path/python/same_hit.py", - }, - { - content: 'from types import NoneType + overwrite: true, + path: "./workspace/some/base/path/ts/mdk.ts", + }, + ], + [ + "3", + { + content: 'from types import NoneType from typing import Callable, List, Union, get_origin, ForwardRef, Any from dataclasses import dataclass, asdict, fields @@ -878,15 +1075,6 @@ class Student(Struct): FORWARD_REFS["Student"] = Student -@dataclass -class TwoInput(Struct): - name: str - - - -FORWARD_REFS["TwoInput"] = TwoInput -TypeString6 = str -Type8Student = List["Student"] def __repr(value: Any): @@ -895,60 +1083,70 @@ def __repr(value: Any): return value -def typed_fnTwo(user_fn: Callable[[TwoInput], TypeString6]): +def typed_three(user_fn: Callable[[Object7], Student]): def exported_wrapper(raw_inp): - inp: TwoInput = Struct.new(TwoInput, raw_inp) - out: TypeString6 = user_fn(inp) + inp: Object7 = Struct.new(Object7, raw_inp) + out: Student = user_fn(inp) if isinstance(out, list): return [__repr(v) for v in out] return __repr(out) return exported_wrapper -def typed_fnOne(user_fn: Callable[[Object7], Type8Student]): - def exported_wrapper(raw_inp): - inp: Object7 = Struct.new(Object7, raw_inp) - out: Type8Student = user_fn(inp) - if isinstance(out, list): - return [__repr(v) for v in out] - return __repr(out) - return exported_wrapper +', + overwrite: true, + path: "./workspace/some/base/path/python/other_types.py", + }, + ], + [ + "4", + { + content: '# - NOTE: only modules that are imported relatively +# 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 typed_three, Object7, Student +@typed_three +def three(inp: Object7) -> Student: + # TODO: write your logic here + raise Exception("three not implemented") + ', - overwrite: true, - path: "./workspace/some/base/path/python/same_hit_types.py", - }, - { - content: " -mod mdk; -pub use mdk::*; + overwrite: false, + path: "./workspace/some/base/path/python/other.py", + }, + ], + [ + "5", + { + content: 'package.name = "example_metagen_mdk" +package.edition = "2021" +package.version = "0.0.1" -/* -init_mat! { - hook: || { - // initialize global stuff here if you need it - MatBuilder::new() - // register function handlers here - .register_handler(stubs::MyFunc::erased(MyMat)) - } -} +[lib] +path = "lib.rs" +crate-type = ["cdylib", "rlib"] -struct MyMat; +[dependencies] +anyhow = "1" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +wit-bindgen = "0.22.0" -// FIXME: use actual types from your mdk here -impl stubs::MyFunc for MyMat { - fn handle(&self, input: types::MyFuncIn, _cx: Ctx) -> anyhow::Result { - unimplemented!() - } -} -*/ -", - overwrite: false, - path: "./workspace/some/base/path/rust/lib.rs", - }, - { - content: \`// This file was @generated by metagen and is intended + +[profile.release] +strip = "symbols" +opt-level = "z"', + overwrite: false, + path: "./workspace/some/base/path/rust/Cargo.toml", + }, + ], + [ + "6", + { + content: \`// This file was @generated by metagen and is intended // to be generated again on subsequent metagen runs. #![cfg_attr(rustfmt, rustfmt_skip)] @@ -1258,184 +1456,34 @@ pub mod stubs { } } \`, - overwrite: true, - path: "./workspace/some/base/path/rust/mdk.rs", - }, - { - content: '// This file was @generated by metagen and is intended -// to be generated again on subsequent metagen runs. - -export type Ctx = { - parent?: Record; - /** - * Request context extracted by auth extractors. - */ - context?: Record; - secrets: Record; - effect: "create" | "update" | "delete" | "read" | undefined | null; - meta: { - url: string; - token: string; - }; - headers: Record; -}; - -/** - * Access features on your typegraph deployment. - */ -export type Deployment = { - gql: (query: readonly string[], ...args: unknown[]) => { - run: ( - variables: Record, - ) => Promise>; - }; -}; - -export type Handler = ( - input: In, - ctx: Ctx, - tg: Deployment, -) => Out | Promise; - -export type Object7 = { - name: string; -}; -export type Student3 = Array; -export type Student43 = Student3 | null | undefined; -export type Student = { - id: number; - name: string; - peers: Student43; -}; -export type Student8 = Array; -export type TwoInput = { - name: string; -}; - - -export type Func18Handler = Handler; -export type Func19Handler = Handler; -export type Func20Handler = Handler; -', - overwrite: true, - path: "./workspace/some/base/path/ts/mdk.ts", - }, - { - content: 'from types import NoneType -from typing import Callable, List, Union, get_origin, ForwardRef, Any -from dataclasses import dataclass, asdict, fields - -FORWARD_REFS = {} - -class Struct(): - def repr(self): - return asdict(self) - - @staticmethod - def try_new(dt_class, val: Any): - # Object - ftypes = {f.name: f.type for f in fields(dt_class)} - attrs = {} - for f in val: - fval = val[f] - ftype = ftypes[f] - serialized = False - # Union - if get_origin(ftype) is Union: - try: - attrs[f] = Struct.try_union(ftype.__args__, fval) - serialized = True - except Exception: - pass - # List - elif get_origin(ftype) is list: - try: - attrs[f] = Struct.try_typed_list(ftype.__args__, fval) - serialized = True - except Exception: - pass - # Any - if not serialized: - if isinstance(ftype, str) and ftype in FORWARD_REFS: - klass = FORWARD_REFS[ftype] - attrs[f] = Struct.new(klass, fval) - else: - attrs[f] = Struct.new(ftype, fval) - return dt_class(**attrs) - - @staticmethod - def try_typed_list(tpe: Any, items: Any): - hint = tpe.__args__[0] - klass = ( - FORWARD_REFS[hint.__forward_arg__] if isinstance(hint, ForwardRef) else hint - ) - return [Struct.new(klass, v) for v in items] - - @staticmethod - def try_union(variants: List[Any], val: Any): - errors = [] - for variant in variants: - try: - if variant is NoneType: - if val is None: - return None - else: - continue - if get_origin(variant) is list: - if isinstance(val, list): - return Struct.try_typed_list(variant, val) - else: - continue - klass = FORWARD_REFS[variant.__forward_arg__] - return Struct.try_new(klass, val) - except Exception as e: - errors.append(str(e)) - raise Exception("\\\\n".join(errors)) - - @staticmethod - def new(dt_class: Any, val: Any): - try: - return Struct.try_new(dt_class, val) - except Exception: - return val - -@dataclass -class Object7(Struct): - name: str - - - -FORWARD_REFS["Object7"] = Object7 -@dataclass -class Student(Struct): - id: int - name: str - peers: Union[List["Student"], None] - - - -FORWARD_REFS["Student"] = Student - - -def __repr(value: Any): - if isinstance(value, Struct): - return value.repr() - return value + overwrite: true, + path: "./workspace/some/base/path/rust/mdk.rs", + }, + ], + [ + "7", + { + content: '# - NOTE: only modules that are imported relatively +# 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 Type8Student, Object7, typed_fnOne, TwoInput, TypeString6, typed_fnTwo -def typed_three(user_fn: Callable[[Object7], Student]): - def exported_wrapper(raw_inp): - inp: Object7 = Struct.new(Object7, raw_inp) - out: Student = user_fn(inp) - if isinstance(out, list): - return [__repr(v) for v in out] - return __repr(out) - return exported_wrapper +@typed_fnOne +def fnOne(inp: Object7) -> Type8Student: + # TODO: write your logic here + raise Exception("fnOne not implemented") +@typed_fnTwo +def fnTwo(inp: TwoInput) -> TypeString6: + # TODO: write your logic here + raise Exception("fnTwo not implemented") ', - overwrite: true, - path: "./workspace/some/base/path/python/other_types.py", - }, + overwrite: false, + path: "./workspace/some/base/path/python/same_hit.py", + }, + ], ] `; diff --git a/typegate/tests/metagen/metagen_test.ts b/typegate/tests/metagen/metagen_test.ts index 70399acb49..306fa7e7bd 100644 --- a/typegate/tests/metagen/metagen_test.ts +++ b/typegate/tests/metagen/metagen_test.ts @@ -128,7 +128,11 @@ Meta.test("Metagen within sdk", async (t) => { const { Metagen } = await import("@typegraph/sdk/metagen.js"); const metagen = new Metagen(workspace, genConfig); const generated = metagen.dryRun(tg, targetName); - await t.assertSnapshot(generated); + await t.assertSnapshot( + Object.entries(generated).sort(([keyA], [keyB]) => + keyA.localeCompare(keyB) + ), + ); sdkResults.push(JSON.stringify(generated, null, 2)); }); @@ -153,7 +157,11 @@ Meta.test("Metagen within sdk", async (t) => { const output = await child.output(); if (output.success) { const generated = JSON.parse(new TextDecoder().decode(output.stdout)); - await t.assertSnapshot(generated); + await t.assertSnapshot( + Object.entries(generated).sort(([keyA], [keyB]) => + keyA.localeCompare(keyB) + ), + ); sdkResults.push(JSON.stringify(generated, null, 2)); } else { @@ -388,7 +396,7 @@ Meta.test("metagen table suite", async (metaTest) => { }, ]; - await metaTest.should("rust tests", async () => { + await metaTest.should("build rust crate", async () => { assertEquals( (await metaTest.shell("bash build.sh".split(" "), { currentDir: genCratePath, diff --git a/typegate/tests/metagen/typegraphs/identities/py/handlers_types.py b/typegate/tests/metagen/typegraphs/identities/py/handlers_types.py index 323687d945..20d3f1e77e 100644 --- a/typegate/tests/metagen/typegraphs/identities/py/handlers_types.py +++ b/typegate/tests/metagen/typegraphs/identities/py/handlers_types.py @@ -116,8 +116,8 @@ class CompositesArgs(Struct): @dataclass class Composites(Struct): opt: Union[str, None] - either: Union["Primitives", "Branch2"] - union: Union[int, str, List[str]] + either: Union["Branch2", "Primitives"] + union: Union[str, int, List[str]] list: List[str] @@ -143,8 +143,8 @@ class Cycles1Args(Struct): @dataclass class Cycles1(Struct): phantom1: Union[str, None] - to2: Union[Union["Cycles1", Union["Branch33A", "Branch33B"]], None] - list3: Union[List[Union["Branch33B", "Branch33A"]], None] + to2: Union[Union[Union["Branch33A", "Branch33B"], "Cycles1"], None] + list3: Union[List[Union["Branch33A", "Branch33B"]], None] FORWARD_REFS["Cycles1"] = Cycles1 @@ -209,10 +209,10 @@ def __repr(value: Any): return value -def typed_cycles(user_fn: Callable[[Cycles1Args], Cycles1]): +def typed_composites(user_fn: Callable[[CompositesArgs], Composites]): def exported_wrapper(raw_inp): - inp: Cycles1Args = Struct.new(Cycles1Args, raw_inp) - out: Cycles1 = user_fn(inp) + inp: CompositesArgs = Struct.new(CompositesArgs, raw_inp) + out: Composites = user_fn(inp) if isinstance(out, list): return [__repr(v) for v in out] return __repr(out) @@ -220,10 +220,10 @@ def exported_wrapper(raw_inp): return exported_wrapper -def typed_primitives(user_fn: Callable[[PrimitivesArgs], Primitives]): +def typed_cycles(user_fn: Callable[[Cycles1Args], Cycles1]): def exported_wrapper(raw_inp): - inp: PrimitivesArgs = Struct.new(PrimitivesArgs, raw_inp) - out: Primitives = user_fn(inp) + inp: Cycles1Args = Struct.new(Cycles1Args, raw_inp) + out: Cycles1 = user_fn(inp) if isinstance(out, list): return [__repr(v) for v in out] return __repr(out) @@ -231,10 +231,10 @@ def exported_wrapper(raw_inp): return exported_wrapper -def typed_composites(user_fn: Callable[[CompositesArgs], Composites]): +def typed_primitives(user_fn: Callable[[PrimitivesArgs], Primitives]): def exported_wrapper(raw_inp): - inp: CompositesArgs = Struct.new(CompositesArgs, raw_inp) - out: Composites = user_fn(inp) + inp: PrimitivesArgs = Struct.new(PrimitivesArgs, raw_inp) + out: Primitives = user_fn(inp) if isinstance(out, list): return [__repr(v) for v in out] return __repr(out) diff --git a/typegate/tests/runtimes/python/python_sync_test.ts b/typegate/tests/runtimes/python/python_sync_test.ts index a18c1700fb..4f23d6aa99 100644 --- a/typegate/tests/runtimes/python/python_sync_test.ts +++ b/typegate/tests/runtimes/python/python_sync_test.ts @@ -1,7 +1,7 @@ // Copyright Metatype OÜ, licensed under the Elastic License 2.0. // SPDX-License-Identifier: Elastic-2.0 -import { gql, Meta } from "test-utils/mod.ts"; +import { gql, Meta } from "../../utils/mod.ts"; import { connect } from "redis"; import { S3Client } from "aws-sdk/client-s3"; import { createBucket, listObjects, tryDeleteBucket } from "test-utils/s3.ts"; @@ -241,87 +241,6 @@ Meta.test( }, ); -Meta.test( - { - name: "Deno: def, lambda in sync mode", - syncConfig, - async setup() { - await cleanUp(); - }, - async teardown() { - await cleanUp(); - }, - }, - async (t) => { - const e = await t.engine("runtimes/python/python.ts"); - - await t.should("work with def", async () => { - await gql` - query { - identityLambda(input: { a: "hello", b: [1, 2, "three"] }) { - a - b - } - } - ` - .expectData({ - identityLambda: { - a: "hello", - b: [1, 2, "three"], - }, - }) - .on(e); - }); - - await t.should("work with def", async () => { - await gql` - query { - identityDef(input: { a: "hello", b: [1, 2, "three"] }) { - a - b - } - } - ` - .expectData({ - identityDef: { - a: "hello", - b: [1, 2, "three"], - }, - }) - .on(e); - }); - }, -); - -Meta.test( - { - name: "Python: infinite loop or similar in sync mode", - sanitizeOps: false, - syncConfig, - async setup() { - await cleanUp(); - }, - async teardown() { - await cleanUp(); - }, - }, - async (t) => { - const e = await t.engine( - "runtimes/python/python.py", - ); - - await t.should("safely fail upon stackoverflow", async () => { - await gql` - query { - stackOverflow(enable: true) - } - ` - .expectErrorContains("maximum recursion depth exceeded") - .on(e); - }); - }, -); - Meta.test( { name: "Python: typegate reloading in sync mode", diff --git a/typegate/tests/runtimes/wasm_wire/rust/build.sh b/typegate/tests/runtimes/wasm_wire/rust/build.sh index 1108281f0c..9bb6d4f6fd 100755 --- a/typegate/tests/runtimes/wasm_wire/rust/build.sh +++ b/typegate/tests/runtimes/wasm_wire/rust/build.sh @@ -9,13 +9,13 @@ cd - ADAPTOR="../../../../../tmp/wasi_snapshot_preview1.reactor.wasm" [ -f "$ADAPTOR" ] || ghjk x install-wasi-adapter -TARGET=wasm32-wasi +TARGET=wasm32-unknown-unknown cargo build --target $TARGET # --release # wasm-opt --enable-bulk-memory -Oz ./target/$TARGET/release/rust.wasm -o ./target/rust-component.wasm.opt wasm-tools component new \ ./target/$TARGET/debug/rust.wasm \ -o ./target/rust-component.wasm \ - --adapt wasi_snapshot_preview1=$ADAPTOR + #--adapt wasi_snapshot_preview1=$ADAPTOR wasm-tools component wit target/rust-component.wasm cp target/rust-component.wasm ../rust.wasm diff --git a/typegraph/core/src/typegraph.rs b/typegraph/core/src/typegraph.rs index 84ce77d061..e5bac57890 100644 --- a/typegraph/core/src/typegraph.rs +++ b/typegraph/core/src/typegraph.rs @@ -212,6 +212,12 @@ pub fn finalize( dynamic: ctx.meta.queries.dynamic, endpoints: Store::get_graphql_endpoints(), }, + // artifacts: { + // let arts = ctx.meta.artifacts; + // let mut arts = arts.into_iter().collect::>(); + // arts.sort_by_cached_key(|pair| pair.0.clone()); + // arts.into_iter().collect() + // }, artifacts: ctx.meta.artifacts, random_seed: Store::get_random_seed(), auths, diff --git a/typegraph/core/src/utils/fs_host.rs b/typegraph/core/src/utils/fs_host.rs index 0f5663158b..b3d03f79f2 100644 --- a/typegraph/core/src/utils/fs_host.rs +++ b/typegraph/core/src/utils/fs_host.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: MPL-2.0 use std::{ - collections::HashSet, + collections::BTreeSet, path::{Path, PathBuf}, }; @@ -261,7 +261,7 @@ pub fn expand_glob(path: &str) -> Result, String> { } pub fn resolve_globs_dirs(deps: Vec) -> Result, String> { - let mut resolved_deps = HashSet::new(); + let mut resolved_deps = BTreeSet::new(); for dep in deps { if is_glob(&dep) { let abs_path = make_absolute(&PathBuf::from(dep))? diff --git a/typegraph/core/src/utils/postprocess/python_rt.rs b/typegraph/core/src/utils/postprocess/python_rt.rs index 44b7a43769..ffd56b0175 100644 --- a/typegraph/core/src/utils/postprocess/python_rt.rs +++ b/typegraph/core/src/utils/postprocess/python_rt.rs @@ -7,7 +7,7 @@ use common::typegraph::{ utils::{map_from_object, object_from_map}, Typegraph, }; -use std::{collections::hash_map::Entry, path::PathBuf}; +use std::{collections::btree_map::Entry, path::PathBuf}; use crate::utils::postprocess::PostProcessor; diff --git a/website/docs/concepts/features-overview/index.mdx b/website/docs/concepts/features-overview/index.mdx index fc4b1b4d82..bb6041535a 100644 --- a/website/docs/concepts/features-overview/index.mdx +++ b/website/docs/concepts/features-overview/index.mdx @@ -591,7 +591,7 @@ Options: --version -v, --verbose... Increase logging verbosity -q, --quiet... De { title: "Deployments", status: "complete", - link: "/docs/reference/programmatic-deployment", + link: "/docs/guides/programmatic-deployment", body: ( <> Programmatically upload your typegraphs to the typegate from within @@ -603,7 +603,7 @@ Options: --version -v, --verbose... Increase logging verbosity -q, --quiet... De { title: "Migrations", status: "beta", - link: "/docs/reference/programmatic-deployment", + link: "/docs/guides/programmatic-deployment", body: ( <> Programmaticaly manage necessary migrations returned by the Prisma @@ -617,7 +617,7 @@ Options: --version -v, --verbose... Increase logging verbosity -q, --quiet... De { title: "Codegen", status: "beta", - link: "/docs/reference/programmatic-deployment", + link: "/docs/guides/programmatic-deployment", body: ( <> Access metagen through the SDK directly to generate type diff --git a/website/docs/guides/external-functions/index.mdx b/website/docs/guides/external-functions/index.mdx index a520238a84..ab32dab288 100644 --- a/website/docs/guides/external-functions/index.mdx +++ b/website/docs/guides/external-functions/index.mdx @@ -3,7 +3,6 @@ sidebar_position: 50 --- import TGExample from "@site/src/components/TGExample"; -import CodeBlock from "@theme-original/CodeBlock"; # Custom functions @@ -29,9 +28,10 @@ The following example uses the `DenoRuntime` to respond to requests and define a Note that for the `fib` root function, we're using a typescript module in an external file. Here's what `scripts/fib.ts` looks like: - - {require("!!code-loader!../../../../examples/typegraphs/scripts/fib.ts").content} - + The following runtimes can be used to run custom functions: - [`DenoRuntime`](/docs/reference/runtimes/deno/) through typescript code. @@ -41,7 +41,7 @@ The following runtimes can be used to run custom functions: ## Accessing function context -:::info Beta +:::warning Beta The following feature is currently only implemented for the `DenoRuntime`. ::: @@ -62,7 +62,7 @@ Consult the reference for each runtime to look at what's availaible. ## Accessing the typegraph -:::info Beta +:::warning Beta The following feature is currently only implemented for the `DenoRuntime`. ::: @@ -81,6 +81,88 @@ The following exapmle illustrates how it functions: And `scripts/createVote.ts` looks like: - - {require("!!code-loader!../../../../examples/typegraphs/scripts/createVote.ts").content} - + +## Generating types + +:::warning Beta +The following feature is not yet stable. +::: + +Custom functions can be made smoother to develop or more robust to changes by using the [metagen](/docs/reference/metagen) to generate code for them. +By using the generated types along with static analysis tools in your workflow, this allows you to avoid many cases of drift between your custom functions and your typegraph. +Not to mention improving the development experience of writing custom functions. +The code generated for Python and Rust targets also includes the serialization boilerplate and utilities that you'd need for those to even get started in those languages. + +The metagen library is availaible within the [meta CLI](/docs/reference/meta-cli) or the [typegraph SDK](/docs/reference/typegraph#sdks). +In the following example we'll use the CLI to generate typescript code for a simple typegraph. + + + +We'll need to configure metagen through the `metatype.yaml` file. We add a new target to the metagen section that does what we need. + +```yaml +metagen: + targets: + # named targets to configure a bunch of generators together + metagen_deno: + - generator: mdk_typescript # generator to use + # path to generate to + path: ./metagen/ts/ + # point to the typegraph location + # or name of typegraph on a typegate somehwere + typegraph_path: ./metagen-deno.ts +``` + +We can configure multiple target in our config file. +Each target can have mutiple generator configurations. +Metagen currently [implements](/docs/reference/metagen) for the Typescript, Python and Rust targets. + +We can then invoke the target using the following command: + +```bash +meta gen metagen_deno +``` + +This results in the following files. + +```bash +❯ lsd --tree metagen + metagen +└──  ts + └──  mdk.ts +```` + +Within `mdk.ts` and the types and helpers, all the types from your typegraph should be there. Something like: + +
+ + +Code generation sample. Collapsed for aesthetic reasons. + + + + +
+ +Note, this also include typescript function types for specific typegraph functions. +By default, the `mdk_typescript` generator will only include stub function types for those defined on the `DenoRuntime` but this is [configurable](/docs/reference/metagen#mdk_typescript). + +We can then use these types in the following manner. Add the following snippet into `metagen/ts/remix.ts`. + + + +Ofcourse, we'll need to include both our implementation and the generated code when defining our typegraph functions. diff --git a/website/docs/guides/external-functions/metagen.graphql b/website/docs/guides/external-functions/metagen.graphql new file mode 100644 index 0000000000..fc69bef1c3 --- /dev/null +++ b/website/docs/guides/external-functions/metagen.graphql @@ -0,0 +1,13 @@ +query { + remix( + title: "Lovefool" + artist: "The Cardigans" + releaseTime: "1996-06-29T10:30:40.340Z" + mp3Url: "https://mus.ic/lovefool.mp3" + ) { + title + artist + releaseTime + mp3Url + } +} diff --git a/website/docs/reference/programmatic-deployment/index.mdx b/website/docs/guides/programmatic-deployment/index.mdx similarity index 100% rename from website/docs/reference/programmatic-deployment/index.mdx rename to website/docs/guides/programmatic-deployment/index.mdx diff --git a/website/docs/guides/wasm-functions/index.mdx b/website/docs/guides/wasm-functions/index.mdx new file mode 100644 index 0000000000..f53c76a555 --- /dev/null +++ b/website/docs/guides/wasm-functions/index.mdx @@ -0,0 +1,148 @@ +--- +sidebar_position: 50 +--- + +import TGExample from "@site/src/components/TGExample"; +import CodeBlock from "@theme-original/CodeBlock"; + +# Wasm functions + +:::warning Beta +The following feature is not yet stable. +::: + +The [`WasmRuntime`](/docs/reference/runtimes/wasm) enables one to use any langauge/ecosystem with a toolchain capable of producing wasm artifacts to author [custom functions](/docs/guides/external-functions). +[Rust](https://rust-lang.org) is one such a language and has shaped up to be the hotspot of development in the wasm ecosystem (The Metatype itself platform has many rusty parts). +In this guide, we'll see how to set up a workflow for using Rust for our custom functions. + +## Tooling + +We need to install several programs to be able to produce the components. +The following checklist contains links to get you started: + +1. Rust compiler toolchain: this guide assumes moderate familiartiy of development with rust and won't spend many words on how to get it functional. In any case, you can get started with rust [here](https://www.rust-lang.org/learn/get-started). +2. `wasm32-unknown-unknown` target for rustc: This is the backend that rustc uses to produce wasi compatible wasm components. If you're using `rustup` to manage your toolchain, Cargo will automatically install the target when you're building. +3. `wasm-tools`: this is the swiss army knife for working with wasm artifacts, courtesy of the [Bytecode Alliance](https://bytecodealliance.org/). Installation instructions can be found [here](https://github.com/bytecodealliance/wasm-tools/). + +## Typegraph + +The `WasmRuntime` currently comes in two flavours that are both based on the wasm component spec. +This guide focues on the `wire` flavour, where your component is expected to implement a standard WIT interface that's designed around a simple Json based RPC wire format. +Thankfully, all of that boilerplate is easy to automate away and that's exactly what we'll do using [metagen](/docs/reference/metagen) to generate the binding code. + +Before anything though, we need to author the typegraph: + + + +Note that the `WasmRuntime` constructor mentions a non-existent wasm file on disk. +This won't be a problem for the metagen generators but we'll need to produce the artifact before we deploy the typegraph. +We'll see what buliding the artifact entails in just a minute. + +## Metagen + +We can now tackle the boilerplate. +Metagen bundles the [`mdk_rust`](/docs/reference/metagen#mdk_rust) generator which can generate all the glue code along with Rust types that correspond to our typegraph types. +Let's configure a metagen target in our configuration file to get just that done. + +```yaml +metagen: + targets: + metagen_rs: + # this is the generator we're interested in + - generator: mdk_rust + # the location where to put the generated files + path: ./metagen/rs/ + # the path to our typegraph + typegraph_path: ./metagen-rs.ts +``` + +The configuration file is read by the [meta CLI](/docs/reference/meta-cli) which also bundles the metagen suite. +This means we can invoke the target from the command line like so: + +```bash +meta gen metagen_rs +``` + +This should give us the following files: + +```shell +❯ lsd --tree metagen/rs/ + rs +├──  Cargo.toml +├──  lib.rs +└──  mdk.rs +``` + +By default, the `mdk_rust` generator outputs all the necessary files required to build our wasm file. +This includes the `Cargo.toml` manifest for our Rust crate. + + + {require("!!code-loader!../../../../examples/typegraphs/metagen/rs/Cargo.toml").content} + + +`mdk_rust` will not overwrite a `Cargo.toml` file discovored at generation path so you can add other dependencies if need be. + +The `mdk.rs` file contains all the glue code including the typegraph types. + +
+ + Code generation sample. + It's collapsed here as it's for the most part an uninteresting implementation detail. + + + {require("!!code-loader!../../../../examples/typegraphs/metagen/rs/mdk.rs").content} + +
+ +When working on the typegraph, we can run metagen again to regenerate this file and get the latest types. + +The generator also includes a sample `lib.rs` entrypoint file for our crate. +We'll modify it now to implement our custom function. + + + {require("!!code-loader!../../../../examples/typegraphs/metagen/rs/lib.rs").content} + + +## Building + +We'll now use the rust toolchain and wasm-tools to build the wasm component. +This requires multiple commands. +It's presented below as a shell script that you can modify from. + +```bash +# flags to make script execution visible +set -eux + +# regenerate code before building +meta gen metagen_rs + +# variablize common names +TARGET=wasm32-wasi +CRATE_NAME=metagen_rs_mdk + +# build in release mode for smallest sizes +cargo build -p $CRATE_NAME --target $TARGET --release +# use wasm-tools to change wasm file into wasm component +wasm-tools component new \ + # rust builds the wasm file under the name of the crate + ./target/$TARGET/debug/$CRATE_NAME.wasm \ + -o ./target/rust-component.wasm \ + +# copy the component to a location that we specified +# in our typegraph +cp ./target/rust-component.wasm ./rust.wasm +``` + +Put the shell script into a file like `build.sh` and execute it with a posix compatible shell like `bash`. +You should now have all the files to deploy your typegraph. + + diff --git a/website/docs/guides/wasm-functions/metagen.graphql b/website/docs/guides/wasm-functions/metagen.graphql new file mode 100644 index 0000000000..fc69bef1c3 --- /dev/null +++ b/website/docs/guides/wasm-functions/metagen.graphql @@ -0,0 +1,13 @@ +query { + remix( + title: "Lovefool" + artist: "The Cardigans" + releaseTime: "1996-06-29T10:30:40.340Z" + mp3Url: "https://mus.ic/lovefool.mp3" + ) { + title + artist + releaseTime + mp3Url + } +} diff --git a/website/docs/reference/changelog.mdx b/website/docs/reference/changelog.mdx index 85be30d7a1..303a9409ed 100644 --- a/website/docs/reference/changelog.mdx +++ b/website/docs/reference/changelog.mdx @@ -1,7 +1,7 @@ --- title: Changelog comments: false -sidebar_position: 9 +sidebar_position: 1000 --- import Changelog from "../../../CHANGELOG.md"; diff --git a/website/docs/reference/ecosystem/index.mdx b/website/docs/reference/ecosystem/index.mdx index 3480b62e24..2dc3a1fd76 100644 --- a/website/docs/reference/ecosystem/index.mdx +++ b/website/docs/reference/ecosystem/index.mdx @@ -10,7 +10,7 @@ sidebar_position: 5 ## ghjk -[ghjk](https://github.com/metatypedev/ghjk) (pronounced /gk/) is a programmable runtime manager for developer environments. It powers the development of Metatype on its authors' machines, on the CI and containers. +[ghjk](https://github.com/metatypedev/ghjk) (pronounced /jk/) is a programmable runtime manager for developer environments. It powers the development of Metatype on its authors' machines, on the CI and containers. ## Lade diff --git a/website/docs/reference/metagen/index.mdx b/website/docs/reference/metagen/index.mdx new file mode 100644 index 0000000000..5d5c3d5c93 --- /dev/null +++ b/website/docs/reference/metagen/index.mdx @@ -0,0 +1,220 @@ +--- +sidebar_position: 50 +--- + +import TGExample from "@site/src/components/TGExample"; +import CodeBlock from "@theme-original/CodeBlock"; + +# Metagen + +:::warning Beta +The following feature is not yet stable. +::: + +Metagen is a code-generator suite that contains implementations that help with development on the Metatype platform. +Today, this means a set of generators to help with [custom functions](/docs/guides/external-functions) by generating types, serializers and bindings. +It's availaible bundled within the [meta CLI](/docs/reference/meta-cli) and the [typegraph SDKs](/docs/reference/typegraph#sdks). + +## Access through CLI + +The meta-cli has a dedicated `gen` command for interacting with metagen. We configure the generators through the [standard configuration file](/docs/reference/meta-cli/configuration-file) under the metagen key. + +```yaml +typegates: + # bla bla + +typegraphs: + # bla bla + +metagen: + targets: + main: + # generator to use + - generator: mdk_rust + # path to generate to + path: ./bff/ + # typegraph path to use + typegraph_path: ./typegraphs/svc-bff.ts + # we can have multiple generators per target + - generator: mdk_rust + path: ./telemetry/ + typegraph_path: ./typegraphs/svc-telemetry.ts + # generators might have custom keys + stubbed_runtimes: ["wasm_wire", "deno"] + # more than one targets avail if you need them + iter: + - generator: mdk_typescript + path: ./ts/ + # name of typegraph to read from typegate + typegraph: svc_products +``` + +This allows us to invoke the targets from the CLI. + +```sh +meta cli gen main +``` + +This will resolve the requisite typegraphs, serialize as needed and put the resulting files at the appropriate locations. +If no target name is provied, the CLI will look for a target under the key `main` and invoke it instead. + +## Access through SDK + +Metagen is availaible through the SDK for programmatic access needs and can be helpful when writing tests or when relying on the CLI is not an option. + + + +## Generators + +:::info Chicken or the egg? +As most of the generators are intended for types to be used by custom functions, they'll require that you declare the custom functions in your typegraph first. +This begs the question, how does one declare custom functions that depend on artifacts that are yet to be generated? +Typegraphs error out when referenced artifacts aren't found, how does it work in this scenario? + +To resolve this concern, the SDKs support a serialization mode that skips resolution of artifacts. +This mode is activated when serialization is done for codegen purposes. +What this means is that, **you can declare non-existent files in your typegraph and codegen should work**. +Some generators are even smart enough to work around your expected files. +Of course, if the files aren't present when you're trying to deply to the typegate, it'll raise an error. +::: + +### `mdk_typescript` + +This generator supports: +- Typescript types that map to typegraph types +- Stub function types for custom functions implementors that adhere to typegraph functions. + - By default, all function types from the `DenoRuntime` get stub types. + - Use `stubbed_runtimes` to select which runtimes get stubs. +- Types for interacting with the typegate from within custom functions. + +The following example showcases the generator. + +Typegraph: + + + +Custom function: + + + +
+ + Code generation sample. + + + +
+ +It supports the following extra configuration keys. + +| Key | Type | Default | Description | +| ------------------ | ---------- | ---------- | ------------------------------------------ | +| `stubbed_runtimes` | `string[]` | `["deno"]` | Runtimes for which to generate stub types. | + +### `mdk_python` + +This generator supports: +- Python classes that map to typegraph types +- Decorators for custom functions implementors that require adherance to typegraph function types. + - By default, all functions from the `PythonRuntime` get stub types. + - TODO: `stubbed_runtimes` for `mdk_python` +- TODO: types for interacting with the typegate from within custom functions. + +If the referenced module for the custom function is not found, the generator will also output stub implementation (in addition to the types) at the given type. +It will not replace our code on a second run. + +The following example showcases the generator. + +Typegraph: + + + +Custom function: + + + +
+ + Code generation sample. + + + +
+ +### `mdk_rust` + +This generator generates types, serializers and bindings needed to implement custom functions in Rust. +Rust implementations will need to be compiled to wasm components to be executed on the metatype platform and the generator assumes such usage. + +To be more specific, it supports: +- Rust types that map to typegraph defined types + - Serialization is handled out of sight through [`serde_json`](https://lib.rs/serde_json) +- Stub traits for custom functions implementors that adhere to typegraph functions. + - By default, all functions from the `WasmRuntime` get stub types. + - The generator assumes the `wire` based wasm interface is being targetted. + - `stubbed_runtimes` key can be used to configure stub generation from additional runtimes. +- Types for interacting with the typegate from within custom functions. +- Glue code for setting up the wasm component to be run within the `WasmRuntime`. + +By default the generator will also output a library crate entrypoint and a functional Cargo.toml with all the required dependencies. +These additional files wlil not be overwritten on a second run. +The generator can also be configured to avoid generating them even if not present. + +The following example showcases the generator. + +Typegraph: + + + +Custom function: + + + {require("!!code-loader!../../../../examples/typegraphs/metagen/rs/lib.rs").content} + + +
+ + Code generation sample. + + + {require("!!code-loader!../../../../examples/typegraphs/metagen/rs/mdk.rs").content} + +
+ +It supports the following extra configuration keys. + +| Key | Type | Default | Description | +| ------------------ | ---------- | ---------------------- | ----------------------------------------------------- | +| `stubbed_runtimes` | `string[]` | `["wasm_wire"]` | Runtimes for which to generate stub types. | +| `crate_name` | `string` | `${typegraphName}_mdk` | Name to assign to crate when generating `Cargo.toml`. | +| `skip_cargo_toml` | `boolean` | `false` | Do not generate `Cargo.toml`. | +| `skip_lib_rs` | `boolean` | `false` | Do not generate `lib.rs`, the sample entrypoint. | + + diff --git a/website/packages/code-loader-transform.js b/website/packages/code-loader-transform.js index 0465f0d6b3..765607e2c7 100644 --- a/website/packages/code-loader-transform.js +++ b/website/packages/code-loader-transform.js @@ -66,7 +66,7 @@ module.exports = async function (source) { } } - const transformation = postTransformations[ext]; + const transformation = postTransformations[ext] ?? ((src) => src); const content = transformation(deindent(ret.join("\n"))).trim(); return `module.exports = ${JSON.stringify({ diff --git a/website/shared/projects/tab-first-project-python.mdx b/website/shared/projects/tab-first-project-python.mdx index be07f13c14..ae02e9ec69 100644 --- a/website/shared/projects/tab-first-project-python.mdx +++ b/website/shared/projects/tab-first-project-python.mdx @@ -103,7 +103,7 @@ Once you started your typegate instance using one of the available choice, if yo running typegate{" "} -To deploy your typegraph to the typegate engine, there are two approaces you can follow. You can either use [self-deploy](/docs/reference/programmatic-deployment) which comes with the typegraph SDK or the [Meta CLI](/docs/reference/meta-cli). For now, you will be deploying your typegraph using the `Meta CLI`. Execute the command below on your terminal to deploy the typegraph. +To deploy your typegraph to the typegate engine, there are two approaces you can follow. You can either use [self-deploy](/docs/guides/programmatic-deployment) which comes with the typegraph SDK or the [Meta CLI](/docs/reference/meta-cli). For now, you will be deploying your typegraph using the `Meta CLI`. Execute the command below on your terminal to deploy the typegraph. ```bash meta deploy -f api/example.py --allow-dirty --create-migration --target dev --gate http://localhost:7890 diff --git a/website/src/components/MiniQL/index.tsx b/website/src/components/MiniQL/index.tsx index d6f1c16bbd..a30411ab39 100644 --- a/website/src/components/MiniQL/index.tsx +++ b/website/src/components/MiniQL/index.tsx @@ -18,7 +18,7 @@ import GraphiQLInterface, { Panel } from "./graphiql"; import * as ast from "graphql/language/ast"; import { MemoryStorage } from "./memory_store"; import { ChoicePicker } from "../ChoicePicker"; -import { useSDK } from "../../states/sdk"; +import { SDK, useSDK } from "../../states/sdk"; import TabItem from "@theme/TabItem"; export interface MiniQLProps { @@ -91,11 +91,20 @@ function MiniQLBrowser({ ? (
[ + lang.codeLanguage ?? "default", + { + typescript: "Typescript", + python: "Python", + }[lang.codeLanguage!] ?? "Default", + ]) ?? [ + ["typescript", "Typescript"], + ["python", "Python"] + ]) + } + choice={code && code.length < 2 ? (code[0].codeLanguage ?? "default") as SDK : sdk} onChange={setSDK} className="ml-2" > diff --git a/whiz.yaml b/whiz.yaml index a6470984b8..b844a7e932 100644 --- a/whiz.yaml +++ b/whiz.yaml @@ -4,6 +4,7 @@ env: RUST_LOG: "typegate=info,typegate_engine=info,mt_deno=info,swc_ecma_codegen=off,tracing::span=off,error" NPM_CONFIG_REGISTRY: http://localhost:4873 WASMTIME_BACKTRACE_DETAILS: "1" + TIMER_MAX_TIMEOUT_MS: "30000" gate1: &tp watch: