diff --git a/examples/deploy/deploy.py b/examples/deploy/deploy.py index 431eb668af..e17d78fee8 100644 --- a/examples/deploy/deploy.py +++ b/examples/deploy/deploy.py @@ -86,6 +86,7 @@ def deploy_example_python(g: Graph): dir=None, prisma_migration=config_params, disable_artifact_resolution=None, + codegen=None, ) diff --git a/meta-cli/src/cli/deploy.rs b/meta-cli/src/cli/deploy.rs index 5020da8727..36aa95d0c1 100644 --- a/meta-cli/src/cli/deploy.rs +++ b/meta-cli/src/cli/deploy.rs @@ -146,6 +146,7 @@ impl Deploy { auth: node.auth.clone(), }); ServerStore::set_prefix(node_config.prefix); + ServerStore::set_codegen_flag(deploy.options.codegen); Ok(Self { config, diff --git a/meta-cli/src/cli/gen.rs b/meta-cli/src/cli/gen.rs index fc8ebe855b..4d87f1782a 100644 --- a/meta-cli/src/cli/gen.rs +++ b/meta-cli/src/cli/gen.rs @@ -1,9 +1,10 @@ // Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. // SPDX-License-Identifier: MPL-2.0 +use std::collections::HashMap; use std::{path::PathBuf, sync::Arc}; -use super::{Action, ConfigArgs, NodeArgs}; +use crate::cli::{Action, ConfigArgs, NodeArgs}; use crate::{ com::store::ServerStore, config::Config, @@ -13,15 +14,37 @@ use actix::Actor; use actix_web::dev::ServerHandle; use anyhow::{bail, Context, Result}; use async_trait::async_trait; -use clap::Parser; +use clap::{Parser, ValueEnum}; use common::typegraph::Typegraph; use metagen::*; +use serde_json::json; + +#[derive(ValueEnum, Debug, Clone)] +enum GeneratorOp { + /// missing module dependencies + Mod, + /// mdk materializer + Mdk, +} -#[derive(Parser, Debug)] +impl From for String { + fn from(op: GeneratorOp) -> String { + match op { + GeneratorOp::Mod => "codegen", + GeneratorOp::Mdk => "mdk_rust", + } + .to_string() + } +} + +#[derive(Parser, Debug, Clone)] pub struct Gen { #[command(flatten)] node: NodeArgs, + #[clap(value_enum, default_value_t=GeneratorOp::Mod)] + generator: GeneratorOp, + /// Target typegate (cf config) #[clap(short, long)] pub target: Option, @@ -29,13 +52,22 @@ pub struct Gen { /// Metagen target to generate #[clap(default_value = "main")] gen_target: String, + + /// Force load a typegraph file + #[clap(short, long)] + file: Option, } #[async_trait] impl Action for Gen { async fn run(&self, args: ConfigArgs, server_handle: Option) -> Result<()> { let dir = args.dir()?; - let config = Config::load_or_find(args.config, &dir)?; + let mut config = Config::load_or_find(args.config, &dir)?; + + if let Some(file) = &self.file { + config.metagen = Some(dummy_gen_cfg(&self.generator, file)); + } + let config = Arc::new(dbg!(config)); let Some(mgen_conf) = &config.metagen else { anyhow::bail!( @@ -43,6 +75,7 @@ impl Action for Gen { config.path ); }; + let node_config = config.node(&self.node, self.target.as_deref().unwrap_or("dev")); let node = node_config .build(&dir) @@ -55,18 +88,40 @@ impl Action for Gen { typegate: Arc::new(node), }; - let files = metagen::generate_target(mgen_conf, &self.gen_target, resolver).await?; - let mut set = tokio::task::JoinSet::new(); - for (path, file) in files { - set.spawn(async move { - tokio::fs::create_dir_all(path.parent().unwrap()).await?; - tokio::fs::write(path, file).await?; - Ok::<_, tokio::io::Error>(()) - }); - } - while let Some(res) = set.join_next().await { - res??; - } + match &self.generator { + GeneratorOp::Mod => { + let Some(file) = &self.file else { + anyhow::bail!("no file provided"); + }; + + resolver + .resolve(GeneratorInputOrder::TypegraphFromPath { + path: file.to_owned(), + name: None, + }) + .await?; + + let responses = ServerStore::get_responses(file) + .context("invalid state, no response received")?; + for (_, res) in responses.iter() { + res.codegen()? + } + } + GeneratorOp::Mdk => { + let files = metagen::generate_target(mgen_conf, &self.gen_target, resolver).await?; + let mut set = tokio::task::JoinSet::new(); + for (path, file) in files { + set.spawn(async move { + tokio::fs::create_dir_all(path.parent().unwrap()).await?; + tokio::fs::write(path, file).await?; + Ok::<_, tokio::io::Error>(()) + }); + } + while let Some(res) = set.join_next().await { + res??; + } + } + }; server_handle.unwrap().stop(true).await; @@ -172,3 +227,21 @@ async fn load_tg_at( Ok(tg) } + +fn dummy_gen_cfg(generator: &GeneratorOp, file: &PathBuf) -> metagen::Config { + let mut targets = HashMap::new(); + targets.insert( + generator.clone().into(), + json!({ + "typegraph_path": file, + "path": "./mats/gen", + "annotate_debug": false, + }), + ); + + let target = metagen::Target(targets); + let mut targets = HashMap::new(); + targets.insert("main".to_string(), target); + + metagen::Config { targets } +} diff --git a/meta-cli/src/codegen/deno.rs b/meta-cli/src/codegen/deno.rs index d54b0bae0a..84bd6dd68c 100644 --- a/meta-cli/src/codegen/deno.rs +++ b/meta-cli/src/codegen/deno.rs @@ -91,7 +91,7 @@ pub struct Codegen<'a> { } impl<'a> Codegen<'a> { - fn new

(tg: &'a Typegraph, path: P) -> Self + pub fn new

(tg: &'a Typegraph, path: P) -> Self where P: AsRef, { @@ -145,6 +145,18 @@ impl<'a> Codegen<'a> { } } + pub fn apply_codegen(self) -> Result<()> { + let modules = self.codegen()?; + for ModuleCode { path, code } in modules.iter() { + if let Some(parent) = path.parent() { + fs::create_dir_all(parent) + .with_context(|| format!("Error creating directory {}", parent.display()))?; + } + fs::write(path, code).with_context(|| format!("Error generating {}", path.display()))? + } + Ok(()) + } + fn codegen(mut self) -> Result> { let mut gen_list = vec![]; @@ -462,69 +474,101 @@ impl IntoJson for HashMap { } } -// #[cfg(test)] -// mod tests { -// use std::sync::Arc; - -// use normpath::PathExt; -// use pathdiff::diff_paths; -// use tokio::sync::mpsc; - -// use super::*; -// use crate::config::Config; -// use crate::deploy::actors::console::ConsoleActor; -// use crate::deploy::actors::loader::{LoadModule, LoaderActor, LoaderEvent}; -// use crate::tests::utils::ensure_venv; -// use actix::prelude::*; - -// #[actix::test(flavor = "multi_thread")] -// async fn codegen() -> Result<()> { -// crate::logger::init(); -// ensure_venv()?; -// let test_folder = Path::new("./src/tests/typegraphs").normalize()?; -// std::env::set_current_dir(&test_folder)?; -// let tests = fs::read_dir(&test_folder).unwrap(); -// let config = Config::default_in("."); -// let config = Arc::new(config); - -// for typegraph_test in tests { -// let typegraph_test = typegraph_test.unwrap().path(); -// let typegraph_test = diff_paths(&typegraph_test, &test_folder).unwrap(); - -// let console = ConsoleActor::new(Arc::clone(&config)).start(); -// let (event_tx, event_rx) = mpsc::unbounded_channel(); -// let loader = LoaderActor::new(Arc::clone(&config), console, event_tx, 1) -// .auto_stop() -// .start(); -// loader.do_send(LoadModule( -// test_folder -// .join(&typegraph_test) -// .as_path() -// .to_owned() -// .into(), -// )); - -// let mut event_rx = event_rx; -// // let tg = match event_rx.recv().await.unwrap() { -// // LoaderEvent::Typegraph(tg) => tg, -// // evt => bail!("unexpected loader evt: {evt:?}"), -// // }; - -// // TODO: -// // run typegraph! thenget serialized version - -// // let module_codes = Codegen::new(&tg, &typegraph_test).codegen()?; -// // assert_eq!(module_codes.len(), 1); - -// // let test_name = typegraph_test.to_string_lossy().to_string(); -// // insta::assert_snapshot!(test_name, &module_codes[0].code); - -// assert!(matches!( -// event_rx.recv().await, -// Some(LoaderEvent::Stopped(_)) -// )); -// } - -// Ok(()) -// } -// } +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use futures::try_join; + use futures::FutureExt; + use normpath::PathExt; + use pathdiff::diff_paths; + use tokio::sync::mpsc; + + use super::*; + use crate::com::server::init_server; + use crate::com::store::ServerStore; + + use crate::com::store::Command; + use crate::config::Config; + use crate::deploy::actors::console::ConsoleActor; + use crate::deploy::actors::loader::{LoadModule, LoaderActor, LoaderEvent}; + use crate::tests::utils::ensure_venv; + use actix::prelude::*; + + #[actix::test(flavor = "multi_thread")] + async fn codegen() -> Result<()> { + crate::logger::init(); + ensure_venv()?; + let test_folder = Path::new("./src/tests/typegraphs").normalize()?; + std::env::set_current_dir(&test_folder)?; + let tests = fs::read_dir(&test_folder).unwrap(); + let config = Config::default_in("."); + let config = Arc::new(config); + + for typegraph_test in tests { + let typegraph_test = typegraph_test.unwrap().path(); + let typegraph_test = diff_paths(&typegraph_test, &test_folder).unwrap(); + + let console = ConsoleActor::new(Arc::clone(&config)).start(); + let (event_tx, event_rx) = mpsc::unbounded_channel(); + let loader = LoaderActor::new(Arc::clone(&config), console, event_tx, 1) + .auto_stop() + .start(); + + let server = init_server()?; + let server_handle = server.handle(); + + ServerStore::with(Some(Command::Serialize), Some(config.as_ref().clone())); + ServerStore::set_codegen_flag(true); // ! + + let test_scope = async { + loader.do_send(LoadModule( + test_folder + .join(&typegraph_test) + .as_path() + .to_owned() + .into(), + )); + + let mut event_rx = event_rx; + let tg = match event_rx.recv().await.unwrap() { + LoaderEvent::Typegraph(tg_infos) => { + let (_name, res) = tg_infos + .get_responses_or_fail()? + .as_ref() + .clone() + .into_iter() + .next() + .unwrap(); + res.as_typegraph()? // ! + } + evt => bail!("unexpected loader evt: {evt:?}"), + }; + server_handle.stop(true).await; + + let module_codes = Codegen::new(&tg, &typegraph_test).codegen()?; + assert_eq!(module_codes.len(), 1); + + let test_name = typegraph_test.to_string_lossy().to_string(); + insta::assert_snapshot!(test_name, &module_codes[0].code); + + assert!(matches!( + event_rx.recv().await, + Some(LoaderEvent::Stopped(_)) + )); + + Ok(()) + }; + + match try_join!(server.map(|_| Ok(())), test_scope) { + Ok(((), ())) => {} + Err(e) => { + server_handle.stop(true).await; + panic!("{}", e) + } + }; + } + + Ok(()) + } +} diff --git a/meta-cli/src/com/responses.rs b/meta-cli/src/com/responses.rs index ce5835819a..6c27b40924 100644 --- a/meta-cli/src/com/responses.rs +++ b/meta-cli/src/com/responses.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: MPL-2.0 use super::store::Command; -use crate::deploy::push::pusher::PushResultRaw; +use crate::{codegen::deno::Codegen, deploy::push::pusher::PushResultRaw}; use anyhow::{bail, Result}; use common::typegraph::Typegraph; use serde::{Deserialize, Serialize}; @@ -26,8 +26,7 @@ pub struct CLIResponseError { #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct SDKResponse { - #[allow(dead_code)] - command: Command, + pub command: Command, pub typegraph_name: String, pub typegraph_path: PathBuf, /// Payload from the SDK (serialized typegraph, response from typegate) @@ -66,4 +65,16 @@ impl SDKResponse { serde_json::from_value(self.data.clone().unwrap())?; response.data("addTypegraph") } + + pub fn typegraph_dir(&self) -> PathBuf { + let mut ret = self.typegraph_path.clone(); + ret.pop(); // pop file.ext + ret + } + + pub fn codegen(&self) -> Result<()> { + let tg = self.as_typegraph()?; + let path = self.typegraph_path.clone(); + Codegen::new(&tg, &path).apply_codegen() + } } diff --git a/meta-cli/src/com/server.rs b/meta-cli/src/com/server.rs index 26ac1a2548..9514ec37bc 100644 --- a/meta-cli/src/com/server.rs +++ b/meta-cli/src/com/server.rs @@ -60,6 +60,7 @@ async fn config(req: HttpRequest) -> impl Responder { let secrets = ServerStore::get_secrets(); let migration_action_glob = ServerStore::get_migration_action_glob(); let disable_artifact_resolution = !ServerStore::get_artifact_resolution_flag(); + let codegen = ServerStore::get_codegen_flag(); let mut migration_action_per_rt = vec![]; if let Some(per_rt_actions) = @@ -97,7 +98,8 @@ async fn config(req: HttpRequest) -> impl Responder { "globalAction": migration_action_glob, "runtimeAction": migration_action_per_rt }, - "disableArtifactResolution": disable_artifact_resolution + "disableArtifactResolution": disable_artifact_resolution, + "codegen": codegen }, }); @@ -132,8 +134,23 @@ async fn command() -> impl Responder { #[post("/response")] async fn response(req_body: String) -> impl Responder { let sdk_response: SDKResponse = serde_json::from_str(&req_body).unwrap(); - // to be used later - ServerStore::add_response(sdk_response.clone()); + + match &sdk_response.command { + super::store::Command::Codegen => { + if let Err(e) = sdk_response.codegen() { + return HttpResponse::Ok() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .json(CLIResponseError { + error: e.to_string(), + }); + } + } + _ => { + // to be used later + ServerStore::add_response(sdk_response.clone()); + } + }; + HttpResponse::Ok() .status(StatusCode::OK) .json(CLIResponseSuccess { diff --git a/meta-cli/src/com/store.rs b/meta-cli/src/com/store.rs index 5dd2a15e21..71e6048172 100644 --- a/meta-cli/src/com/store.rs +++ b/meta-cli/src/com/store.rs @@ -36,6 +36,7 @@ fn with_store_mut T>(f: F) -> T { pub enum Command { Deploy, Serialize, + Codegen, } #[derive(Default, Clone, Debug)] @@ -69,6 +70,7 @@ pub struct ServerStore { prefix: Option, sdk_responses: HashMap>>, artifact_resolution: Option, + codegen: Option, } #[allow(dead_code)] @@ -186,4 +188,12 @@ impl ServerStore { pub fn get_artifact_resolution_flag() -> bool { with_store(|s| s.artifact_resolution.unwrap_or(true)) } + + pub fn set_codegen_flag(value: bool) { + with_store_mut(|s| s.codegen = Some(value)) + } + + pub fn get_codegen_flag() -> bool { + with_store(|s| s.codegen.unwrap_or(false)) + } } diff --git a/meta-cli/src/main.rs b/meta-cli/src/main.rs index 2ab187c38a..807a932cc0 100644 --- a/meta-cli/src/main.rs +++ b/meta-cli/src/main.rs @@ -22,7 +22,7 @@ use clap::Parser; use cli::upgrade::upgrade_check; use cli::Action; use cli::Args; -use com::server::{get_instance_port, init_server}; +use com::server::init_server; use futures::try_join; use futures::FutureExt; use log::{error, warn}; @@ -69,8 +69,6 @@ fn main() -> Result<()> { .enable_all() .build()? .block_on(async { - std::env::set_var("META_CLI_SERVER_PORT", get_instance_port().to_string()); - let server = init_server().unwrap(); let command = gen_args.run(args.config, Some(server.handle())); @@ -83,8 +81,6 @@ fn main() -> Result<()> { Some(command) => actix::run(async move { match command { cli::Commands::Serialize(_) | cli::Commands::Dev(_) | cli::Commands::Deploy(_) => { - std::env::set_var("META_CLI_SERVER_PORT", get_instance_port().to_string()); - let server = init_server().unwrap(); let command = command.run(args.config, Some(server.handle())); diff --git a/meta-cli/src/tests/typegraphs/codegen.py b/meta-cli/src/tests/typegraphs/codegen.py index 8d4830ca2d..07e9dbd970 100644 --- a/meta-cli/src/tests/typegraphs/codegen.py +++ b/meta-cli/src/tests/typegraphs/codegen.py @@ -22,7 +22,7 @@ def math(g: Graph): }, name="Output", ), - module="/inexisting/path/to/ts/module.ts", + module="inexisting/path/to/ts/module.ts", name="div", ).with_policy(public) ) diff --git a/meta-cli/src/typegraph/loader/mod.rs b/meta-cli/src/typegraph/loader/mod.rs index e561d5e9b5..41f4e1dda0 100644 --- a/meta-cli/src/typegraph/loader/mod.rs +++ b/meta-cli/src/typegraph/loader/mod.rs @@ -22,7 +22,7 @@ use anyhow::{anyhow, Error, Result}; use colored::Colorize; use crate::{ - com::{responses::SDKResponse, store::ServerStore}, + com::{responses::SDKResponse, server::get_instance_port, store::ServerStore}, config::ModuleType, utils::ensure_venv, }; @@ -105,6 +105,7 @@ impl<'a> Loader<'a> { let p = command .borrow_mut() .env("META_CLI_TG_PATH", path.display().to_string()) + .env("META_CLI_SERVER_PORT", get_instance_port().to_string()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output() diff --git a/typegate/tests/metagen/metagen_test.ts b/typegate/tests/metagen/metagen_test.ts index 16012b4fa9..d8af611e68 100644 --- a/typegate/tests/metagen/metagen_test.ts +++ b/typegate/tests/metagen/metagen_test.ts @@ -51,7 +51,7 @@ members = ["mdk/"] env: { MCLI_LOADER_CMD: "deno run -A --config ../deno.jsonc", }, - }, ...`-C ${tmpDir} gen`.split(" "))).code, + }, ...`-C ${tmpDir} gen mdk`.split(" "))).code, 0, ); assertEquals( diff --git a/typegraph/core/src/global_store.rs b/typegraph/core/src/global_store.rs index 385b6fab92..c1741a7b08 100644 --- a/typegraph/core/src/global_store.rs +++ b/typegraph/core/src/global_store.rs @@ -64,6 +64,7 @@ pub struct Store { random_seed: Option, latest_alias_no: u32, + codegen_flag: Option, } impl Store { @@ -508,6 +509,16 @@ impl Store { pub fn get_auths() -> Vec { with_store(|s| s.auths.clone()) } + + pub fn set_codegen_flag(status: Option) { + with_store_mut(|s| { + s.codegen_flag = status; + }) + } + + pub fn get_codegen_flag() -> bool { + with_store(|s| s.codegen_flag.unwrap_or(false)) + } } /// Generate a pub fn for asserting/unwrapping a Type as a specific TypeDef variant diff --git a/typegraph/core/src/utils/fs_host.rs b/typegraph/core/src/utils/fs_host.rs index cf60bab11d..72d512ed5b 100644 --- a/typegraph/core/src/utils/fs_host.rs +++ b/typegraph/core/src/utils/fs_host.rs @@ -6,7 +6,8 @@ use std::path::{Path, PathBuf}; use crate::{ global_store::Store, wit::metatype::typegraph::host::{ - expand_path as expand_path_host, get_cwd, path_exists, read_file, write_file, + expand_path as expand_path_host, get_cwd, path_exists as path_exists_host, read_file, + write_file, }, }; use common::archive::{ @@ -158,10 +159,10 @@ pub fn compress_and_encode_base64(path: PathBuf) -> Result { /// Search for .tgignore file at `cwd`, if nothing is found, an empty `Vec` is returned pub fn load_tg_ignore_file() -> Result, String> { - let file = cwd()?.join(".tgignore").display().to_string(); + let file = cwd()?.join(".tgignore"); match path_exists(&file)? { - true => read_text_file(file).map(|content| { + true => read_text_file(file.to_string_lossy()).map(|content| { content .lines() .filter_map(|line| { @@ -206,3 +207,7 @@ pub fn hash_file(path: &Path) -> Result { sha256.update(bytes); Ok(format!("{:x}", sha256.finalize())) } + +pub fn path_exists(path: &Path) -> Result { + path_exists_host(&path.to_string_lossy()) +} diff --git a/typegraph/core/src/utils/postprocess/deno_rt.rs b/typegraph/core/src/utils/postprocess/deno_rt.rs index 9dd33fe0ba..09ae7769e3 100644 --- a/typegraph/core/src/utils/postprocess/deno_rt.rs +++ b/typegraph/core/src/utils/postprocess/deno_rt.rs @@ -1,7 +1,7 @@ // Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. // SPDX-License-Identifier: MPL-2.0 -use crate::utils::fs_host; +use crate::{global_store::Store, utils::fs_host}; use common::typegraph::{ runtimes::deno::ModuleMatData, utils::{map_from_object, object_from_map}, @@ -39,7 +39,18 @@ impl DenoProcessor { // if relative => make it absolute // fs::canonicalize wouldn't work in this setup let main_path = fs_host::make_absolute(&PathBuf::from(path))?; - mat_data.code = compress_and_encode(&main_path)?; + + match fs_host::path_exists(&main_path)? { + true => mat_data.code = compress_and_encode(&main_path)?, + false => { + if !Store::get_codegen_flag() { + return Err(format!( + "could not resolve module {:?}", + main_path.display() + )); + } // else cli codegen + } + } mat.data = map_from_object(mat_data).map_err(|e| e.to_string())?; diff --git a/typegraph/core/src/utils/postprocess/mod.rs b/typegraph/core/src/utils/postprocess/mod.rs index d4d8b692b6..e46756d6e9 100644 --- a/typegraph/core/src/utils/postprocess/mod.rs +++ b/typegraph/core/src/utils/postprocess/mod.rs @@ -37,6 +37,8 @@ impl PostProcessor for TypegraphPostProcessor { fn postprocess(self, tg: &mut Typegraph) -> Result<(), TgError> { if let Some(config) = self.config { Store::set_deploy_cwd(config.dir); // fs_host::cwd() will now use this value + Store::set_codegen_flag(config.codegen); + PrismaProcessor::new(config.prisma_migration).postprocess(tg)?; // Artifact resolution depends on the default cwd() (parent process) diff --git a/typegraph/core/wit/typegraph.wit b/typegraph/core/wit/typegraph.wit index 0e780465f1..fd7fff6922 100644 --- a/typegraph/core/wit/typegraph.wit +++ b/typegraph/core/wit/typegraph.wit @@ -50,7 +50,8 @@ interface core { dir: option, // cwd() if none prefix: option, // overrides tg.meta.prefix field if defined disable-artifact-resolution: option, // any postprocess using fs ops should be disabled when set to true - prisma-migration: migration-config + prisma-migration: migration-config, + codegen: option // if set, sdk should not throw errors when the script does not exist } finalize-typegraph: func(config: option) -> result>>, error> diff --git a/typegraph/node/sdk/src/tg_manage.ts b/typegraph/node/sdk/src/tg_manage.ts index 30dbba98f7..ff2623e0c8 100644 --- a/typegraph/node/sdk/src/tg_manage.ts +++ b/typegraph/node/sdk/src/tg_manage.ts @@ -3,13 +3,13 @@ import { ArtifactResolutionConfig } from "./gen/interfaces/metatype-typegraph-core.js"; import { BasicAuth, tgDeploy, tgRemove } from "./tg_deploy.js"; -import { TypegraphOutput } from "./typegraph.js"; +import { TgFinalizationResult, TypegraphOutput } from "./typegraph.js"; import { getEnvVariable } from "./utils/func_utils.js"; const PORT = "META_CLI_SERVER_PORT"; // meta-cli instance that executes the current file const SELF_PATH = "META_CLI_TG_PATH"; // path to the current file to uniquely identify the run results -type Command = "serialize" | "deploy"; +type Command = "serialize" | "deploy" | "codegen"; // Types for CLI => SDK type CLIServerResponse = { @@ -29,6 +29,7 @@ type CLIConfigRequest = { secrets: Record; artifactsConfig: ArtifactResolutionConfig; disableArtifactResolution: boolean; + codegen: boolean; }; type CLISuccess = { @@ -128,12 +129,28 @@ export class Manager { await this.#relayResultToCLI( "deploy", async () => { - const { typegate } = await tgDeploy(this.#typegraph, { + const config = { + ...artifactsConfig, + prefix, + }; + + // hack for allowing tg.serialize(config) to be called more than once + let localMemo = this.#typegraph.serialize(config); + const reusableTgOutput = { + ...this.#typegraph, + serialize: (_: ArtifactResolutionConfig) => localMemo, + } as TypegraphOutput; + + if (artifactsConfig.codegen) { + await this.#relayResultToCLI( + "codegen", + async () => JSON.parse(localMemo.tgJson), + ); + } + + const { typegate } = await tgDeploy(reusableTgOutput, { baseUrl: endpoint, - artifactsConfig: { - ...artifactsConfig, - prefix, - }, + artifactsConfig: config, secrets, auth: new BasicAuth(auth.username, auth.password), }); diff --git a/typegraph/node/sdk/src/typegraph.ts b/typegraph/node/sdk/src/typegraph.ts index f7c10149f2..5d64ad71c0 100644 --- a/typegraph/node/sdk/src/typegraph.ts +++ b/typegraph/node/sdk/src/typegraph.ts @@ -221,12 +221,21 @@ export async function typegraph( const ret = { serialize(config: ArtifactResolutionConfig) { - const [tgJson, ref_artifacts] = core.finalizeTypegraph(config); - const result: TgFinalizationResult = { - tgJson: tgJson, - ref_artifacts: ref_artifacts, - }; - return result; + try { + const [tgJson, ref_artifacts] = core.finalizeTypegraph(config); + const result: TgFinalizationResult = { + tgJson: tgJson, + ref_artifacts: ref_artifacts, + }; + return result; + } catch (err) { + const stack = (err as any)?.payload?.stack; + if (stack) { + // FIXME: jco generated code throws new Error(object) => prints [Object object] + throw new Error(stack.join("\n")); + } + throw err; + } }, name, } as TypegraphOutput; diff --git a/typegraph/python/typegraph/graph/tg_manage.py b/typegraph/python/typegraph/graph/tg_manage.py index 9b4dbcf9aa..9473cb623e 100644 --- a/typegraph/python/typegraph/graph/tg_manage.py +++ b/typegraph/python/typegraph/graph/tg_manage.py @@ -27,6 +27,7 @@ class Command(Enum): SERIALIZE = "serialize" DEPLOY = "deploy" + CODEGEN = "codegen" # Types for CLI => SDK @@ -103,7 +104,17 @@ def fn(): artifacts_config=artifacts_config, secrets=config.secrets, ) - ret = tg_deploy(self.typegraph, params) + + local_memo = self.typegraph.serialize(artifacts_config) + reusable_tg_output = TypegraphOutput( + name=self.typegraph.name, serialize=lambda _: local_memo + ) + if artifacts_config.codegen: + self.relay_result_to_cli( + initiator=Command.CODEGEN, fn=lambda: json.loads(local_memo.tgJson) + ) + + ret = tg_deploy(reusable_tg_output, params) return ret.typegate return self.relay_result_to_cli(initiator=Command.DEPLOY, fn=fn) @@ -158,6 +169,7 @@ def request_config(self) -> CLIConfigRequest: disable_artifact_resolution=artifact_config_raw[ "disableArtifactResolution" ], + codegen=artifact_config_raw["codegen"], ), ) diff --git a/website/static/specs/0.0.3.json b/website/static/specs/0.0.3.json index 627b6cea2d..5f90fd6b58 100644 --- a/website/static/specs/0.0.3.json +++ b/website/static/specs/0.0.3.json @@ -2273,15 +2273,21 @@ "TemporalRuntimeData": { "type": "object", "required": [ - "host", + "host_secret", "name" ], "properties": { "name": { "type": "string" }, - "host": { + "host_secret": { "type": "string" + }, + "namespace_secret": { + "type": [ + "string", + "null" + ] } } },