Skip to content

Commit

Permalink
feat(dev): typegraph explorer (#859)
Browse files Browse the repository at this point in the history
- Add a web version of tree-view which is more interactive
- Enable typegraph serialization without `metatype.yml` config file


![image](https://github.com/user-attachments/assets/81771c07-1f2a-493a-81df-969c8182f9bf)
  • Loading branch information
Natoandro authored Sep 30, 2024
1 parent 5725409 commit 3b2e53d
Show file tree
Hide file tree
Showing 9 changed files with 499 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/meta-cli/src/cli/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl Deploy {
let dir: Arc<Path> = args.dir()?.into();

let config_path = args.config.clone();
let config = Arc::new(Config::load_or_find(config_path, &dir)?);
let config = Arc::new(Config::load_or_find(config_path.as_deref(), &dir)?);

let options = deploy.options.clone();

Expand Down
2 changes: 1 addition & 1 deletion src/meta-cli/src/cli/doctor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl Action for Doctor {
}
println!();

let config = Config::load_or_find(args.config.clone(), dir);
let config = Config::load_or_find(args.config.as_deref(), dir);

ui::title("Project", w);
match config {
Expand Down
2 changes: 1 addition & 1 deletion src/meta-cli/src/cli/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl Action for Gen {
#[tracing::instrument]
async fn run(&self, args: ConfigArgs) -> Result<()> {
let dir = args.dir()?;
let config = Config::load_or_find(args.config, &dir)?;
let config = Config::load_or_find(args.config.as_deref(), &dir)?;
let config = Arc::new(config);

let Some(mgen_conf) = &config.metagen else {
Expand Down
2 changes: 1 addition & 1 deletion src/meta-cli/src/cli/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl Action for List {
async fn run(&self, args: ConfigArgs) -> Result<()> {
let dir = args.dir()?;
let config_path = args.config.clone();
let config = Arc::new(Config::load_or_find(config_path, &dir)?);
let config = Arc::new(Config::load_or_find(config_path.as_deref(), &dir)?);

let mut node_config = config.node(&self.node, &self.target);
if self.target == "dev" {
Expand Down
6 changes: 5 additions & 1 deletion src/meta-cli/src/cli/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ impl Action for Serialize {
let dir = args.dir()?;
let config_path = args.config.clone();

let config = Config::load_or_find(config_path, &dir)?;
let config =
Config::load_or_find(config_path.as_deref(), &dir).or_else(|e| match config_path {
Some(_) => Err(e),
None => Ok(Config::default_in(&dir)),
})?;

let config = Arc::new(config);

Expand Down
2 changes: 1 addition & 1 deletion src/meta-cli/src/cli/undeploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl Action for Undeploy {
async fn run(&self, args: super::ConfigArgs) -> Result<()> {
let dir = args.dir()?;
let config_path = args.config.clone();
let config = Config::load_or_find(config_path, &dir)?;
let config = Config::load_or_find(config_path.as_deref(), &dir)?;
let node_config = config.node(&self.node, &self.target);
let node = node_config.build(&dir).await?;

Expand Down
11 changes: 6 additions & 5 deletions src/meta-cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,12 +269,12 @@ impl Config {

/// Load config file:
/// if the config_path is None, search the config file recursively on parent directories.
pub fn load_or_find<P: AsRef<Path>>(
config_path: Option<PathBuf>,
search_start_dir: P,
pub fn load_or_find(
config_path: Option<&Path>,
search_start_dir: impl AsRef<Path>,
) -> Result<Config> {
if let Some(path) = config_path {
Config::from_file(&path).with_context(|| format!("config file not found at {path:?}"))
Config::from_file(path).with_context(|| format!("config file not found at {path:?}"))
} else {
Ok(Config::find(search_start_dir)?
.ok_or_else(|| ferr!("could not find config file"))?)
Expand Down Expand Up @@ -334,6 +334,7 @@ impl Config {
pub fn dir(&self) -> Result<&Path> {
self.path
.as_deref()
.or(Some(self.base_dir.as_path()))
.ok_or_else(|| ferr!("config path required"))?
.parent()
.ok_or_else(|| ferr!("config path has no parent"))
Expand All @@ -351,7 +352,7 @@ mod tests {
fn load_missing_config_file() -> Result<()> {
let project_root = get_project_root()?;
let path = project_root.join("path/to/metatype.yml");
let load_res = Config::load_or_find(Some(path), project_root);
let load_res = Config::load_or_find(Some(&path), project_root);
assert_err_contains(load_res, "config file not found at");
Ok(())
}
Expand Down
94 changes: 94 additions & 0 deletions tools/tree-view-web.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/bin/env -S ghjk deno run -A

// Copyright Metatype OÜ, licensed under the Elastic License 2.0.
// SPDX-License-Identifier: Elastic-2.0

/**
* Usage:
* deno run -A dev/tree-view.ts [<options>] <file.py>
*/

import { TypeGraphDS } from "../src/typegate/src/typegraph/mod.ts";
import { projectDir } from "./utils.ts";
import { join, parseArgs } from "./deps.ts";

const dirname = import.meta.dirname ?? new URL(".", import.meta.url).pathname;
const indexHtml = join(dirname, "tree-view/index.html");

const args = parseArgs(Deno.args, {
string: ["port"],
});

const argsPort = parseInt(args.port ?? "");
const envPort = parseInt(Deno.env.get("PORT") ?? "");
const port = isNaN(argsPort) ? (isNaN(envPort) ? 0 : envPort) : argsPort;

const files = args._ as string[];
if (files.length === 0) {
throw new Error("Path to typegraph definition module is required.");
}
const cmdBase = [
"cargo",
"run",
"--manifest-path",
`${projectDir}/Cargo.toml`,
"-p",
"meta-cli",
"--",
"serialize",
"-f",
];
const tgs: TypeGraphDS[] = [];
for (const file of files) {
const cmd = [...cmdBase, file];
const { stdout, code } = await new Deno.Command(cmd[0], {
args: cmd.slice(1),
stdout: "piped",
stderr: "inherit",
}).output();
if (code !== 0) {
console.log(
`[error] command ${
cmd.map((c) => JSON.stringify(c)).join(" ")
} failed with code ${code}`,
);
continue;
}
tgs.push(...JSON.parse(new TextDecoder().decode(stdout)));
}

if (tgs.length === 0) {
console.log("[error] no typegraph found");
Deno.exit(1);
}

Deno.serve({
port,
onListen({ port, hostname }) {
console.log(
`server running at http://${hostname ?? "localhost"}:${port}`,
);
},
}, async (req) => {
const url = new URL(req.url);
const pathname = url.pathname;
console.log(`[info] method=${req.method} url=${req.url}`);

if (req.method !== "GET") {
console.log(`[error] method '${req.method}' not allowed`);
return new Response("Method not allowed", { status: 405 });
}
if (pathname === "/") {
return new Response(await Deno.readTextFile(indexHtml), {
headers: { "content-type": "text/html" },
});
}
// TODO typegraph list and typegraph by name
if (pathname === "/tg.json") {
return new Response(JSON.stringify(tgs), {
headers: { "content-type": "application/json" },
});
}
console.log(`[error] path '${pathname}' not found`);
return new Response("Not found", { status: 404 });
});
Loading

0 comments on commit 3b2e53d

Please sign in to comment.