mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
feat: add graphql support (#2014)
* feat: add graphql support * fix: use custom editor for viewing graphql schema * fix: graphql parser cargo version * fix: add graphql where missing
This commit is contained in:
Generated
+13
@@ -7265,6 +7265,17 @@ dependencies = [
|
||||
"windmill-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-graphql"
|
||||
version = "1.143.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"windmill-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.143.0"
|
||||
@@ -7331,6 +7342,7 @@ dependencies = [
|
||||
"windmill-parser",
|
||||
"windmill-parser-bash",
|
||||
"windmill-parser-go",
|
||||
"windmill-parser-graphql",
|
||||
"windmill-parser-py",
|
||||
"windmill-parser-sql",
|
||||
"windmill-parser-ts",
|
||||
@@ -7413,6 +7425,7 @@ dependencies = [
|
||||
"windmill-parser",
|
||||
"windmill-parser-bash",
|
||||
"windmill-parser-go",
|
||||
"windmill-parser-graphql",
|
||||
"windmill-parser-py",
|
||||
"windmill-parser-py-imports",
|
||||
"windmill-parser-sql",
|
||||
|
||||
@@ -85,6 +85,7 @@ windmill-parser-py-imports = { path = "./parsers/windmill-parser-py-imports" }
|
||||
windmill-parser-go = { path = "./parsers/windmill-parser-go" }
|
||||
windmill-parser-bash = { path = "./parsers/windmill-parser-bash" }
|
||||
windmill-parser-sql = { path = "./parsers/windmill-parser-sql" }
|
||||
windmill-parser-graphql = { path = "./parsers/windmill-parser-graphql" }
|
||||
|
||||
axum = { version = "^0", features = ["headers"] }
|
||||
headers = "^0"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add up migration script here
|
||||
ALTER TYPE SCRIPT_LANG ADD VALUE IF NOT EXISTS 'graphql';
|
||||
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "windmill-parser-graphql"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "windmill_parser_graphql"
|
||||
path = "./src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
windmill-parser.workspace = true
|
||||
anyhow.workspace = true
|
||||
regex.workspace = true
|
||||
lazy_static.workspace = true
|
||||
serde_json.workspace = true
|
||||
@@ -0,0 +1,111 @@
|
||||
#![allow(non_snake_case)] // TODO: switch to parse_* function naming
|
||||
|
||||
use anyhow::anyhow;
|
||||
use regex::Regex;
|
||||
use serde_json::json;
|
||||
|
||||
use windmill_parser::{Arg, MainArgSignature, Typ};
|
||||
|
||||
pub fn parse_graphql_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
let parsed = parse_graphql_file(&code)?;
|
||||
if let Some(x) = parsed {
|
||||
let args = x;
|
||||
Ok(MainArgSignature { star_args: false, star_kwargs: false, args })
|
||||
} else {
|
||||
Err(anyhow!("Error parsing sql".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref RE_ARG_GRAPHQL: Regex = Regex::new(r#"\$(\w+)\s*:\s*(?:(\w+)!?|\[(\w+)!?\])!?\s*(?:=\s*(\w+)\s*)?"#).unwrap();
|
||||
static ref RE_ARG_GRAPHQL_ARRAY: Regex = Regex::new(r#"^\[(\w+)!?\]!?$"#).unwrap();
|
||||
}
|
||||
|
||||
fn parse_graphql_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
|
||||
let mut args: Vec<Arg> = vec![];
|
||||
|
||||
for cap in RE_ARG_GRAPHQL.captures_iter(code) {
|
||||
let name = cap.get(1).map(|x| x.as_str().to_string()).unwrap();
|
||||
let mut typ = cap.get(2).map(|x| x.as_str().to_string());
|
||||
|
||||
let parsed_typ = if typ.is_none() {
|
||||
let inner_typ = cap.get(3).map(|x| x.as_str().to_string());
|
||||
typ = inner_typ.clone().map(|x| format!("[{}]", x.to_string()));
|
||||
Typ::List(Box::new(parse_graphql_typ(inner_typ.unwrap().as_str())))
|
||||
} else {
|
||||
parse_graphql_typ(typ.clone().unwrap().as_str())
|
||||
};
|
||||
|
||||
let default = cap.get(4).map(|x| x.as_str().to_string());
|
||||
|
||||
let has_default = default.is_some();
|
||||
|
||||
let parsed_default = default.and_then(|x| match parsed_typ {
|
||||
Typ::Int => x.parse::<i64>().ok().map(|x| json!(x)),
|
||||
Typ::Float => x.parse::<f64>().ok().map(|x| json!(x)),
|
||||
_ => Some(json!(x)),
|
||||
});
|
||||
args.push(Arg {
|
||||
name,
|
||||
typ: parsed_typ,
|
||||
default: parsed_default,
|
||||
otyp: Some(typ.unwrap()),
|
||||
has_default,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Some(args))
|
||||
}
|
||||
|
||||
pub fn parse_graphql_typ(typ: &str) -> Typ {
|
||||
match typ {
|
||||
"String" | "ID" => Typ::Str(None),
|
||||
"Int" => Typ::Int,
|
||||
"Boolean" => Typ::Bool,
|
||||
"Float" => Typ::Float,
|
||||
_ => Typ::Object(vec![]),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_graphql_sig() -> anyhow::Result<()> {
|
||||
let code = r#"
|
||||
query($s: String, $arr: [String]) {
|
||||
books {
|
||||
title
|
||||
}
|
||||
}
|
||||
"#;
|
||||
//println!("{}", serde_json::to_string()?);
|
||||
assert_eq!(
|
||||
parse_graphql_sig(code)?,
|
||||
MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args: vec![
|
||||
Arg {
|
||||
otyp: Some("String".to_string()),
|
||||
name: "s".to_string(),
|
||||
typ: Typ::Str(None),
|
||||
default: None,
|
||||
has_default: false
|
||||
},
|
||||
Arg {
|
||||
otyp: Some("[String]".to_string()),
|
||||
name: "arr".to_string(),
|
||||
typ: Typ::List(Box::new(Typ::Str(None))),
|
||||
default: None,
|
||||
has_default: false
|
||||
},
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ windmill-parser-bash.workspace = true
|
||||
windmill-parser-sql.workspace = true
|
||||
windmill-parser-py.workspace = true
|
||||
windmill-parser-ts.workspace = true
|
||||
windmill-parser-graphql.workspace = true
|
||||
wasm-bindgen.workspace = true
|
||||
serde_json.workspace = true
|
||||
getrandom = { workspace = true, features = ["js"] }
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"collaborators": [
|
||||
"Ruben Fiszel <ruben@windmill.dev>"
|
||||
],
|
||||
"version": "1.138.1",
|
||||
"version": "1.141.0",
|
||||
"files": [
|
||||
"windmill_parser_wasm_bg.wasm",
|
||||
"windmill_parser_wasm.js",
|
||||
|
||||
@@ -40,6 +40,11 @@ export function parse_bigquery(code: string): string;
|
||||
* @returns {string}
|
||||
*/
|
||||
export function parse_snowflake(code: string): string;
|
||||
/**
|
||||
* @param {string} code
|
||||
* @returns {string}
|
||||
*/
|
||||
export function parse_graphql(code: string): string;
|
||||
|
||||
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
||||
|
||||
@@ -53,6 +58,7 @@ export interface InitOutput {
|
||||
readonly parse_mysql: (a: number, b: number, c: number) => void;
|
||||
readonly parse_bigquery: (a: number, b: number, c: number) => void;
|
||||
readonly parse_snowflake: (a: number, b: number, c: number) => void;
|
||||
readonly parse_graphql: (a: number, b: number, c: number) => void;
|
||||
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
||||
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
||||
readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
|
||||
|
||||
@@ -381,6 +381,29 @@ export function parse_snowflake(code) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} code
|
||||
* @returns {string}
|
||||
*/
|
||||
export function parse_graphql(code) {
|
||||
let deferred2_0;
|
||||
let deferred2_1;
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
const ptr0 = passStringToWasm0(code, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
wasm.parse_graphql(retptr, ptr0, len0);
|
||||
var r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||
var r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||
deferred2_0 = r0;
|
||||
deferred2_1 = r1;
|
||||
return getStringFromWasm0(r0, r1);
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function handleError(f, args) {
|
||||
try {
|
||||
return f.apply(this, args);
|
||||
@@ -423,7 +446,7 @@ async function __wbg_load(module, imports) {
|
||||
function __wbg_get_imports() {
|
||||
const imports = {};
|
||||
imports.wbg = {};
|
||||
imports.wbg.__wbg_eval_d972bbef37d2cd5a = function(arg0, arg1) {
|
||||
imports.wbg.__wbg_eval_9331e2bd3095f3d6 = function(arg0, arg1) {
|
||||
const ret = eval(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
|
||||
Binary file not shown.
@@ -9,6 +9,7 @@ export function parse_sql(a: number, b: number, c: number): void;
|
||||
export function parse_mysql(a: number, b: number, c: number): void;
|
||||
export function parse_bigquery(a: number, b: number, c: number): void;
|
||||
export function parse_snowflake(a: number, b: number, c: number): void;
|
||||
export function parse_graphql(a: number, b: number, c: number): void;
|
||||
export function __wbindgen_malloc(a: number, b: number): number;
|
||||
export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number;
|
||||
export function __wbindgen_add_to_stack_pointer(a: number): number;
|
||||
|
||||
@@ -49,3 +49,8 @@ pub fn parse_bigquery(code: &str) -> String {
|
||||
pub fn parse_snowflake(code: &str) -> String {
|
||||
wrap_sig(windmill_parser_sql::parse_snowflake_sig(code))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn parse_graphql(code: &str) -> String {
|
||||
wrap_sig(windmill_parser_graphql::parse_graphql_sig(code))
|
||||
}
|
||||
|
||||
@@ -1478,6 +1478,7 @@ async fn tarball_workspace(
|
||||
ScriptLang::Mysql => "my.sql",
|
||||
ScriptLang::Bigquery => "bq.sql",
|
||||
ScriptLang::Snowflake => "sf.sql",
|
||||
ScriptLang::Graphql => "gql",
|
||||
ScriptLang::Nativets => "fetch.ts",
|
||||
ScriptLang::Bun => "bun.ts",
|
||||
};
|
||||
|
||||
@@ -35,6 +35,7 @@ pub enum ScriptLang {
|
||||
Mysql,
|
||||
Bigquery,
|
||||
Snowflake,
|
||||
Graphql,
|
||||
}
|
||||
|
||||
impl ScriptLang {
|
||||
@@ -50,6 +51,7 @@ impl ScriptLang {
|
||||
ScriptLang::Mysql => "mysql",
|
||||
ScriptLang::Bigquery => "bigquery",
|
||||
ScriptLang::Snowflake => "snowflake",
|
||||
ScriptLang::Graphql => "graphql",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ lazy_static::lazy_static! {
|
||||
"postgresql".to_string(),
|
||||
"bigquery".to_string(),
|
||||
"snowflake".to_string(),
|
||||
"graphql".to_string(),
|
||||
"dependency".to_string(),
|
||||
"flow".to_string(),
|
||||
"hub".to_string(),
|
||||
|
||||
@@ -30,6 +30,7 @@ windmill-parser-py.workspace = true
|
||||
windmill-parser-py-imports.workspace = true
|
||||
windmill-parser-bash.workspace = true
|
||||
windmill-parser-sql.workspace = true
|
||||
windmill-parser-graphql.workspace = true
|
||||
sqlx.workspace = true
|
||||
uuid.workspace = true
|
||||
tracing.workspace = true
|
||||
|
||||
@@ -6,7 +6,7 @@ use windmill_queue::HTTP_CLIENT;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{get_content, transform_json_value, AuthedClient, JobCompleted};
|
||||
use crate::{transform_json_value, AuthedClient, JobCompleted};
|
||||
|
||||
use gcp_auth::{AuthenticationManager, CustomServiceAccount};
|
||||
|
||||
@@ -54,7 +54,7 @@ struct BigqueryError {
|
||||
pub async fn do_bigquery(
|
||||
job: QueuedJob,
|
||||
client: &AuthedClient,
|
||||
db: &sqlx::Pool<sqlx::Postgres>,
|
||||
query: &str,
|
||||
) -> windmill_common::error::Result<JobCompleted> {
|
||||
let args = if let Some(args) = &job.args {
|
||||
Some(transform_json_value("args", client, &job.workspace_id, args.clone()).await?)
|
||||
@@ -94,8 +94,6 @@ pub async fn do_bigquery(
|
||||
|
||||
let mut statement_values: Vec<Value> = vec![];
|
||||
|
||||
let query = get_content(&job, db).await?;
|
||||
|
||||
let sig = parse_bigquery_sig(&query)
|
||||
.map_err(|x| Error::ExecutionErr(x.to_string()))?
|
||||
.args;
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
use serde_json::{json, Value};
|
||||
use windmill_common::error::Error;
|
||||
use windmill_common::jobs::QueuedJob;
|
||||
use windmill_queue::HTTP_CLIENT;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{transform_json_value, AuthedClient, JobCompleted};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct GraphqlDatabase {
|
||||
bearer_token: Option<String>,
|
||||
base_url: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct GraphqlResponse {
|
||||
data: Option<Value>,
|
||||
errors: Option<Vec<GraphqlError>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct GraphqlError {
|
||||
message: String,
|
||||
}
|
||||
|
||||
pub async fn do_graphql(
|
||||
job: QueuedJob,
|
||||
client: &AuthedClient,
|
||||
query: &str,
|
||||
) -> windmill_common::error::Result<JobCompleted> {
|
||||
let args = if let Some(args) = &job.args {
|
||||
Some(transform_json_value("args", client, &job.workspace_id, args.clone()).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let graphql_args: serde_json::Value = serde_json::from_value(args.unwrap_or_else(|| json!({})))
|
||||
.map_err(|e| Error::ExecutionErr(e.to_string()))?;
|
||||
let database = serde_json::from_value::<GraphqlDatabase>(
|
||||
graphql_args.get("database").unwrap_or(&json!({})).clone(),
|
||||
)
|
||||
.map_err(|e: serde_json::Error| Error::ExecutionErr(e.to_string()))?;
|
||||
|
||||
let args = &job
|
||||
.args
|
||||
.clone()
|
||||
.unwrap_or_else(|| json!({}))
|
||||
.as_object()
|
||||
.map(|x| x.to_owned())
|
||||
.unwrap_or_else(|| json!({}).as_object().unwrap().to_owned());
|
||||
|
||||
let mut request = HTTP_CLIENT.post(database.base_url).json(&json!({
|
||||
"query": query,
|
||||
"variables": args
|
||||
}));
|
||||
|
||||
if let Some(token) = &database.bearer_token {
|
||||
request = request.bearer_auth(token.as_str());
|
||||
}
|
||||
|
||||
let response = request
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| Error::ExecutionErr(e.to_string()))?;
|
||||
|
||||
let result = response
|
||||
.json::<GraphqlResponse>()
|
||||
.await
|
||||
.map_err(|e| Error::ExecutionErr(e.to_string()))?;
|
||||
|
||||
if let Some(errors) = result.errors {
|
||||
return Err(Error::ExecutionErr(
|
||||
errors
|
||||
.into_iter()
|
||||
.map(|x| x.message)
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
));
|
||||
}
|
||||
|
||||
// And then check that we got back the same string we sent over.
|
||||
return Ok(JobCompleted {
|
||||
job: job,
|
||||
result: result.data.unwrap_or(json!({})),
|
||||
logs: "".to_string(),
|
||||
success: true,
|
||||
});
|
||||
}
|
||||
@@ -6,6 +6,7 @@ mod snowflake_executor;
|
||||
mod common;
|
||||
mod global_cache;
|
||||
mod go_executor;
|
||||
mod graphql_executor;
|
||||
mod js_eval;
|
||||
mod mysql_executor;
|
||||
mod pg_executor;
|
||||
|
||||
@@ -12,7 +12,7 @@ use windmill_queue::HTTP_CLIENT;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{get_content, transform_json_value, AuthedClient, JobCompleted};
|
||||
use crate::{transform_json_value, AuthedClient, JobCompleted};
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Claims {
|
||||
@@ -63,7 +63,7 @@ struct SnowflakeError {
|
||||
pub async fn do_snowflake(
|
||||
job: QueuedJob,
|
||||
client: &AuthedClient,
|
||||
db: &sqlx::Pool<sqlx::Postgres>,
|
||||
query: &str,
|
||||
) -> windmill_common::error::Result<JobCompleted> {
|
||||
let args = if let Some(args) = &job.args {
|
||||
Some(transform_json_value("args", client, &job.workspace_id, args.clone()).await?)
|
||||
@@ -114,8 +114,6 @@ pub async fn do_snowflake(
|
||||
.map(|x| x.to_owned())
|
||||
.unwrap_or_else(|| json!({}).as_object().unwrap().to_owned());
|
||||
|
||||
let query = get_content(&job, db).await?;
|
||||
|
||||
let mut bindings = serde_json::Map::new();
|
||||
let sig = parse_snowflake_sig(&query)
|
||||
.map_err(|x| Error::ExecutionErr(x.to_string()))?
|
||||
|
||||
@@ -66,7 +66,7 @@ use windmill_queue::{add_completed_job, add_completed_job_error,IDLE_WORKERS};
|
||||
use crate::{
|
||||
worker_flow::{
|
||||
handle_flow, update_flow_status_after_job_completion, update_flow_status_in_progress,
|
||||
}, python_executor::{create_dependencies_dir, pip_compile, handle_python_job, handle_python_reqs}, common::{read_result, set_logs}, go_executor::{handle_go_job, install_go_dependencies}, js_eval::{transpile_ts, eval_fetch_timeout}, pg_executor::do_postgresql, mysql_executor::do_mysql,
|
||||
}, python_executor::{create_dependencies_dir, pip_compile, handle_python_job, handle_python_reqs}, common::{read_result, set_logs}, go_executor::{handle_go_job, install_go_dependencies}, js_eval::{transpile_ts, eval_fetch_timeout}, pg_executor::do_postgresql, mysql_executor::do_mysql, graphql_executor::do_graphql,
|
||||
};
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
@@ -361,6 +361,7 @@ pub async fn run_worker<R: rsmq_async::RsmqConnection + Send + Sync + Clone + 's
|
||||
Some(ScriptLang::Mysql),
|
||||
Some(ScriptLang::Bigquery),
|
||||
Some(ScriptLang::Snowflake),
|
||||
Some(ScriptLang::Graphql),
|
||||
Some(ScriptLang::Bun)];
|
||||
|
||||
let worker_execution_duration: HashMap<_, _> = all_langs.clone().into_iter().map(|x| (x.clone(), prometheus::register_histogram!(
|
||||
@@ -1343,7 +1344,7 @@ async fn handle_code_execution_job(
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
{
|
||||
let jc = do_bigquery(job.clone(), &client.get_authed().await, &db).await?;
|
||||
let jc = do_bigquery(job.clone(), &client.get_authed().await, &inner_content).await?;
|
||||
return Ok(jc.result)
|
||||
}
|
||||
} else if language == Some(ScriptLang::Snowflake) {
|
||||
@@ -1354,9 +1355,12 @@ async fn handle_code_execution_job(
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
{
|
||||
let jc = do_snowflake(job.clone(), &client.get_authed().await, &db).await?;
|
||||
let jc = do_snowflake(job.clone(), &client.get_authed().await, &inner_content).await?;
|
||||
return Ok(jc.result)
|
||||
}
|
||||
} else if language == Some(ScriptLang::Graphql) {
|
||||
let jc = do_graphql(job.clone(), &client.get_authed().await, &inner_content).await?;
|
||||
return Ok(jc.result)
|
||||
} else if language == Some(ScriptLang::Nativets) {
|
||||
logs.push_str("\n--- FETCH TS EXECUTION ---\n");
|
||||
let code = format!("const BASE_URL = '{base_internal_url}';\nconst WM_TOKEN = '{}';\n{}", &client.get_token().await, inner_content);
|
||||
@@ -2244,6 +2248,7 @@ async fn capture_dependency_job(
|
||||
ScriptLang::Mysql => Ok("".to_owned()),
|
||||
ScriptLang::Bigquery => Ok("".to_owned()),
|
||||
ScriptLang::Snowflake => Ok("".to_owned()),
|
||||
ScriptLang::Graphql => Ok("".to_owned()),
|
||||
ScriptLang::Bash => Ok("".to_owned()),
|
||||
ScriptLang::Nativets => Ok("".to_owned()),
|
||||
|
||||
|
||||
@@ -64,6 +64,8 @@ async function dev(opts: GlobalOptions & { filter?: string }) {
|
||||
: splitted.length > 2 && splitted[splitted.length - 2] == "sf"
|
||||
? "snowflake"
|
||||
: "postgresql"
|
||||
: ext == "gql"
|
||||
? "graphql"
|
||||
: "unknown";
|
||||
currentLastEdit = {
|
||||
content,
|
||||
|
||||
@@ -137,6 +137,7 @@ function ZipFSElement(zip: JSZip, useYaml: boolean): DynFSElement {
|
||||
else if (language == "mysql") ext = "my.sql";
|
||||
else if (language == "bigquery") ext = "bq.sql";
|
||||
else if (language == "snowflake") ext = "sf.sql";
|
||||
else if (language == "graphql") ext = "gql";
|
||||
else if (language == "bun") ext = "bun.ts";
|
||||
else if (language == "nativets") ext = "native.ts";
|
||||
|
||||
|
||||
Generated
+113
-10
@@ -26,9 +26,11 @@
|
||||
"diff": "^5.1.0",
|
||||
"esm-env": "^1.0.0",
|
||||
"fast-equals": "^5.0.1",
|
||||
"graphql": "^16.7.1",
|
||||
"highlight.js": "^11.8.0",
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-svelte": "^0.246.0",
|
||||
"monaco-graphql": "^1.3.0",
|
||||
"monaco-languageclient": "~6.0.3",
|
||||
"openai": "^4.0.0-beta.4",
|
||||
"quill": "^1.3.7",
|
||||
@@ -41,7 +43,7 @@
|
||||
"svelte-timezone-picker": "^2.0.3",
|
||||
"tailwind-merge": "^1.13.2",
|
||||
"vscode-ws-jsonrpc": "3.0.0",
|
||||
"windmill-parser-wasm": "^1.138.1",
|
||||
"windmill-parser-wasm": "^1.141.0",
|
||||
"y-monaco": "^0.1.4",
|
||||
"y-websocket": "^1.5.0",
|
||||
"yjs": "^13.6.7"
|
||||
@@ -99,6 +101,7 @@
|
||||
"tslib": "^2.6.0",
|
||||
"typescript": "^5.1.3",
|
||||
"vite": "^4.4.7",
|
||||
"vite-plugin-monaco-editor": "^1.1.0",
|
||||
"yootils": "^0.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -4342,6 +4345,29 @@
|
||||
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/graphql": {
|
||||
"version": "16.7.1",
|
||||
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.7.1.tgz",
|
||||
"integrity": "sha512-DRYR9tf+UGU0KOsMcKAlXeFfX89UiiIZ0dRU3mR0yJfu6OjZqUcp68NnFLnqQU5RexygFoDy1EW+ccOYcPfmHg==",
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/graphql-language-service": {
|
||||
"version": "5.1.7",
|
||||
"resolved": "https://registry.npmjs.org/graphql-language-service/-/graphql-language-service-5.1.7.tgz",
|
||||
"integrity": "sha512-xkawYMJeoNYGhT+SpSH3c2qf6HpGHQ/duDmrseVHBpVCrXAiGnliXGSCC4jyMGgZQ05GytsZ12p0nUo7s6lSSw==",
|
||||
"dependencies": {
|
||||
"nullthrows": "^1.0.0",
|
||||
"vscode-languageserver-types": "^3.17.1"
|
||||
},
|
||||
"bin": {
|
||||
"graphql": "dist/temp-bin.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"graphql": "^15.5.0 || ^16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hammerjs": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
|
||||
@@ -6315,6 +6341,20 @@
|
||||
"integrity": "sha512-zhbZ2Nx93tLR8aJmL2zI1mhJpsl87HMebNBM6R8z4pLfs8pj604pIVIVwyF1TivcfNtIPpMXL+nb3DsBmE/x6Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/monaco-graphql": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/monaco-graphql/-/monaco-graphql-1.3.0.tgz",
|
||||
"integrity": "sha512-Ilz7kUKYfO5KGwandjR+eQbN+fj1fQY9OECIQlzgeWOkLXMZqXTsxf01iQfVw08mMsBz7qa6fZKsESouDcBN2g==",
|
||||
"dependencies": {
|
||||
"graphql-language-service": "^5.1.7",
|
||||
"picomatch-browser": "^2.2.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"graphql": "^15.5.0 || ^16.0.0",
|
||||
"monaco-editor": ">= 0.20.0 < 1",
|
||||
"prettier": "^2.8.0 || ^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/monaco-languageclient": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/monaco-languageclient/-/monaco-languageclient-6.0.3.tgz",
|
||||
@@ -6564,6 +6604,11 @@
|
||||
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/nullthrows": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz",
|
||||
"integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw=="
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
@@ -6911,6 +6956,17 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/picomatch-browser": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/picomatch-browser/-/picomatch-browser-2.2.6.tgz",
|
||||
"integrity": "sha512-0ypsOQt9D4e3hziV8O4elD9uN0z/jtUEfxVRtNaAAtXIyUx9m/SzlO020i8YNL2aL/E6blOvvHQcin6HZlFy/w==",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
@@ -7549,7 +7605,6 @@
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
},
|
||||
@@ -9616,6 +9671,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vite-plugin-monaco-editor": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vite-plugin-monaco-editor/-/vite-plugin-monaco-editor-1.1.0.tgz",
|
||||
"integrity": "sha512-IvtUqZotrRoVqwT0PBBDIZPNraya3BxN/bfcNfnxZ5rkJiGcNtO5eAOWWSgT7zullIAEqQwxMU83yL9J5k7gww==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"monaco-editor": ">=0.33.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vitefu": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.4.tgz",
|
||||
@@ -9777,9 +9841,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/windmill-parser-wasm": {
|
||||
"version": "1.138.1",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm/-/windmill-parser-wasm-1.138.1.tgz",
|
||||
"integrity": "sha512-KGbiKKk7i8xRV+kcGXNRChIVXRQRAQ6eAGVJo7c2qjq+Sft3cGteWbkgeh8gjU5+KdmnP866jGwsRtw+zW2+5A=="
|
||||
"version": "1.141.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm/-/windmill-parser-wasm-1.141.0.tgz",
|
||||
"integrity": "sha512-2U4wAyKOMa7YTcWGUSCCqhNEfI+dVF9JvlXzPneOUO9nD7zm5uXRB+xaJLHKtYB5gmzLz1cQElp/FpbJaLLZsw=="
|
||||
},
|
||||
"node_modules/wordwrap": {
|
||||
"version": "1.0.0",
|
||||
@@ -13005,6 +13069,20 @@
|
||||
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
|
||||
"dev": true
|
||||
},
|
||||
"graphql": {
|
||||
"version": "16.7.1",
|
||||
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.7.1.tgz",
|
||||
"integrity": "sha512-DRYR9tf+UGU0KOsMcKAlXeFfX89UiiIZ0dRU3mR0yJfu6OjZqUcp68NnFLnqQU5RexygFoDy1EW+ccOYcPfmHg=="
|
||||
},
|
||||
"graphql-language-service": {
|
||||
"version": "5.1.7",
|
||||
"resolved": "https://registry.npmjs.org/graphql-language-service/-/graphql-language-service-5.1.7.tgz",
|
||||
"integrity": "sha512-xkawYMJeoNYGhT+SpSH3c2qf6HpGHQ/duDmrseVHBpVCrXAiGnliXGSCC4jyMGgZQ05GytsZ12p0nUo7s6lSSw==",
|
||||
"requires": {
|
||||
"nullthrows": "^1.0.0",
|
||||
"vscode-languageserver-types": "^3.17.1"
|
||||
}
|
||||
},
|
||||
"hammerjs": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
|
||||
@@ -14357,6 +14435,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"monaco-graphql": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/monaco-graphql/-/monaco-graphql-1.3.0.tgz",
|
||||
"integrity": "sha512-Ilz7kUKYfO5KGwandjR+eQbN+fj1fQY9OECIQlzgeWOkLXMZqXTsxf01iQfVw08mMsBz7qa6fZKsESouDcBN2g==",
|
||||
"requires": {
|
||||
"graphql-language-service": "^5.1.7",
|
||||
"picomatch-browser": "^2.2.6"
|
||||
}
|
||||
},
|
||||
"monaco-languageclient": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/monaco-languageclient/-/monaco-languageclient-6.0.3.tgz",
|
||||
@@ -14524,6 +14611,11 @@
|
||||
"boolbase": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"nullthrows": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz",
|
||||
"integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw=="
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
@@ -14794,6 +14886,11 @@
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true
|
||||
},
|
||||
"picomatch-browser": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/picomatch-browser/-/picomatch-browser-2.2.6.tgz",
|
||||
"integrity": "sha512-0ypsOQt9D4e3hziV8O4elD9uN0z/jtUEfxVRtNaAAtXIyUx9m/SzlO020i8YNL2aL/E6blOvvHQcin6HZlFy/w=="
|
||||
},
|
||||
"pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
@@ -15183,8 +15280,7 @@
|
||||
"prettier": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||
"dev": true
|
||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="
|
||||
},
|
||||
"prettier-plugin-svelte": {
|
||||
"version": "2.10.1",
|
||||
@@ -16641,6 +16737,13 @@
|
||||
"rollup": "^3.25.2"
|
||||
}
|
||||
},
|
||||
"vite-plugin-monaco-editor": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vite-plugin-monaco-editor/-/vite-plugin-monaco-editor-1.1.0.tgz",
|
||||
"integrity": "sha512-IvtUqZotrRoVqwT0PBBDIZPNraya3BxN/bfcNfnxZ5rkJiGcNtO5eAOWWSgT7zullIAEqQwxMU83yL9J5k7gww==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"vitefu": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.4.tgz",
|
||||
@@ -16766,9 +16869,9 @@
|
||||
}
|
||||
},
|
||||
"windmill-parser-wasm": {
|
||||
"version": "1.138.1",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm/-/windmill-parser-wasm-1.138.1.tgz",
|
||||
"integrity": "sha512-KGbiKKk7i8xRV+kcGXNRChIVXRQRAQ6eAGVJo7c2qjq+Sft3cGteWbkgeh8gjU5+KdmnP866jGwsRtw+zW2+5A=="
|
||||
"version": "1.141.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm/-/windmill-parser-wasm-1.141.0.tgz",
|
||||
"integrity": "sha512-2U4wAyKOMa7YTcWGUSCCqhNEfI+dVF9JvlXzPneOUO9nD7zm5uXRB+xaJLHKtYB5gmzLz1cQElp/FpbJaLLZsw=="
|
||||
},
|
||||
"wordwrap": {
|
||||
"version": "1.0.0",
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
"tslib": "^2.6.0",
|
||||
"typescript": "^5.1.3",
|
||||
"vite": "^4.4.7",
|
||||
"vite-plugin-monaco-editor": "^1.1.0",
|
||||
"yootils": "^0.3.1"
|
||||
},
|
||||
"type": "module",
|
||||
@@ -89,9 +90,11 @@
|
||||
"diff": "^5.1.0",
|
||||
"esm-env": "^1.0.0",
|
||||
"fast-equals": "^5.0.1",
|
||||
"graphql": "^16.7.1",
|
||||
"highlight.js": "^11.8.0",
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-svelte": "^0.246.0",
|
||||
"monaco-graphql": "^1.3.0",
|
||||
"monaco-languageclient": "~6.0.3",
|
||||
"openai": "^4.0.0-beta.4",
|
||||
"quill": "^1.3.7",
|
||||
@@ -104,7 +107,7 @@
|
||||
"svelte-timezone-picker": "^2.0.3",
|
||||
"tailwind-merge": "^1.13.2",
|
||||
"vscode-ws-jsonrpc": "3.0.0",
|
||||
"windmill-parser-wasm": "^1.138.1",
|
||||
"windmill-parser-wasm": "^1.141.0",
|
||||
"y-monaco": "^0.1.4",
|
||||
"y-websocket": "^1.5.0",
|
||||
"yjs": "^13.6.7"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { JobService, Preview } from '$lib/gen'
|
||||
import { dbSchema, dbSchemaPublicOnly, workspaceStore, type DBSchema } from '$lib/stores'
|
||||
import { dbSchema, workspaceStore, type DBSchema, type GraphqlSchema } from '$lib/stores'
|
||||
import { onDestroy } from 'svelte'
|
||||
import Button from './common/button/Button.svelte'
|
||||
import Drawer from './common/drawer/Drawer.svelte'
|
||||
@@ -9,6 +9,8 @@
|
||||
import { tryEvery } from '$lib/utils'
|
||||
import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte'
|
||||
import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import { buildClientSchema, printSchema } from 'graphql'
|
||||
import GraphqlSchemaViewer from './GraphqlSchemaViewer.svelte'
|
||||
|
||||
export let resourceType: string | undefined
|
||||
export let resourcePath: String | undefined = undefined
|
||||
@@ -104,6 +106,27 @@ export async function main(args: any) {
|
||||
}, {});
|
||||
}
|
||||
return data;
|
||||
}`,
|
||||
graphql: `import { getIntrospectionQuery } from "npm:graphql@16.7.1";
|
||||
export async function main(args: any) {
|
||||
const headers: { [key: string]: string } = {
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
if (args.bearer_token) {
|
||||
headers["authorization"] = "Bearer " + args.bearer_token;
|
||||
}
|
||||
const response = await fetch(args.base_url, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
query: getIntrospectionQuery(),
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error("Could not query schema");
|
||||
}
|
||||
const schema = (await response.json()).data;
|
||||
return schema;
|
||||
}`
|
||||
}
|
||||
|
||||
@@ -131,7 +154,23 @@ export async function main(args: any) {
|
||||
if (!testResult.success) {
|
||||
console.error(testResult.result?.['error']?.['message'])
|
||||
} else {
|
||||
dbSchema.set(testResult.result)
|
||||
if (resourceType === 'postgresql') {
|
||||
dbSchema.set({
|
||||
lang: 'postgresql',
|
||||
schema: testResult.result,
|
||||
publicOnly: true
|
||||
})
|
||||
} else if (resourceType === 'mysql') {
|
||||
dbSchema.set({
|
||||
lang: 'mysql',
|
||||
schema: testResult.result
|
||||
})
|
||||
} else if (resourceType === 'graphql') {
|
||||
dbSchema.set({
|
||||
lang: 'graphql',
|
||||
schema: testResult.result
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
timeoutCode: async () => {
|
||||
@@ -153,25 +192,24 @@ export async function main(args: any) {
|
||||
})
|
||||
}
|
||||
|
||||
function formatSchema(
|
||||
schema: DBSchema,
|
||||
resourceType: string | undefined,
|
||||
dbSchemaPublicOnly: boolean
|
||||
) {
|
||||
if (resourceType === 'postgresql' && dbSchemaPublicOnly) {
|
||||
return schema.public || schema
|
||||
} else if (resourceType === 'mysql' && Object.keys(schema).length === 1) {
|
||||
return schema[Object.keys(schema)[0]]
|
||||
function formatSchema(dbSchema: DBSchema) {
|
||||
if (dbSchema.lang === 'postgresql' && dbSchema.publicOnly) {
|
||||
return dbSchema.schema.public || dbSchema
|
||||
} else if (dbSchema.lang === 'mysql' && Object.keys(dbSchema.schema).length === 1) {
|
||||
return dbSchema.schema[Object.keys(dbSchema.schema)[0]]
|
||||
} else {
|
||||
return schema
|
||||
return dbSchema.schema
|
||||
}
|
||||
}
|
||||
|
||||
$: resourcePath && ['postgresql', 'mysql'].includes(resourceType || '') && getSchema()
|
||||
function formatGraphqlSchema(dbSchema: GraphqlSchema) {
|
||||
return printSchema(buildClientSchema(dbSchema.schema))
|
||||
}
|
||||
|
||||
$: resourcePath && ['postgresql', 'mysql', 'graphql'].includes(resourceType || '') && getSchema()
|
||||
|
||||
function clearSchema() {
|
||||
dbSchema.set(undefined)
|
||||
dbSchemaPublicOnly.set(true)
|
||||
}
|
||||
|
||||
$: !resourcePath && $dbSchema && clearSchema()
|
||||
@@ -192,13 +230,17 @@ export async function main(args: any) {
|
||||
</Button>
|
||||
<Drawer bind:this={drawer} size="800px">
|
||||
<DrawerContent title="DB Schema Explorer" on:close={drawer.closeDrawer}>
|
||||
{#if resourceType === 'postgresql'}
|
||||
<ToggleButtonGroup class="mb-4" bind:selected={$dbSchemaPublicOnly}>
|
||||
{#if $dbSchema.lang === 'postgresql'}
|
||||
<ToggleButtonGroup class="mb-4" bind:selected={$dbSchema.publicOnly}>
|
||||
<ToggleButton value={true} label="Public" />
|
||||
<ToggleButton value={false} label="All" />
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
<ObjectViewer json={formatSchema($dbSchema, resourceType, $dbSchemaPublicOnly)} pureViewer />
|
||||
{#if $dbSchema.lang === 'graphql'}
|
||||
<GraphqlSchemaViewer code={formatGraphqlSchema($dbSchema)} class="h-full" />
|
||||
{:else}
|
||||
<ObjectViewer json={formatSchema($dbSchema)} pureViewer />
|
||||
{/if}
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
{/if}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
import 'monaco-editor/esm/vs/basic-languages/shell/shell.contribution'
|
||||
import 'monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution'
|
||||
import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution'
|
||||
import 'monaco-editor/esm/vs/basic-languages/graphql/graphql.contribution'
|
||||
import 'monaco-editor/esm/vs/language/typescript/monaco.contribution'
|
||||
import { MonacoLanguageClient, initServices } from 'monaco-languageclient'
|
||||
import { toSocket, WebSocketMessageReader, WebSocketMessageWriter } from 'vscode-ws-jsonrpc'
|
||||
@@ -41,6 +42,8 @@
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { UserService } from '$lib/gen'
|
||||
import type { Text } from 'yjs'
|
||||
import { initializeMode } from 'monaco-graphql/esm/initializeMode'
|
||||
import type { MonacoGraphQLAPI } from 'monaco-graphql/esm/api'
|
||||
|
||||
let divEl: HTMLDivElement | null = null
|
||||
let editor: meditor.IStandaloneCodeEditor
|
||||
@@ -96,6 +99,7 @@
|
||||
let nbWsAttempt = 0
|
||||
let disposeMethod: () => void | undefined
|
||||
const dispatch = createEventDispatcher()
|
||||
let graphqlService: MonacoGraphQLAPI | undefined = undefined
|
||||
|
||||
const uri =
|
||||
lang == 'typescript'
|
||||
@@ -196,105 +200,116 @@
|
||||
|
||||
let command: Disposable | undefined = undefined
|
||||
|
||||
let dbSchemaCompletor: Disposable | undefined = undefined
|
||||
$: $dbSchema && addDBSchemaCompletions()
|
||||
$: !$dbSchema && dbSchemaCompletor && dbSchemaCompletor.dispose()
|
||||
let sqlSchemaCompletor: Disposable | undefined = undefined
|
||||
$: $dbSchema && ['sql', 'graphql'].includes(lang) && addDBSchemaCompletions()
|
||||
$: (!$dbSchema || lang !== 'sql') && sqlSchemaCompletor && sqlSchemaCompletor.dispose()
|
||||
$: (!$dbSchema || lang !== 'graphql') && graphqlService && graphqlService.setSchemaConfig([])
|
||||
|
||||
function addDBSchemaCompletions() {
|
||||
if (dbSchemaCompletor) {
|
||||
dbSchemaCompletor.dispose()
|
||||
const { lang: schemaLang, schema } = $dbSchema || {}
|
||||
if (!schemaLang || !schema) {
|
||||
return
|
||||
}
|
||||
dbSchemaCompletor = languages.registerCompletionItemProvider('sql', {
|
||||
triggerCharacters: ['.', ' ', '('],
|
||||
provideCompletionItems: function (model, position) {
|
||||
const textUntilPosition = model.getValueInRange({
|
||||
startLineNumber: 1,
|
||||
startColumn: 1,
|
||||
endLineNumber: position.lineNumber,
|
||||
endColumn: position.column
|
||||
})
|
||||
|
||||
const word = model.getWordUntilPosition(position)
|
||||
const range = {
|
||||
startLineNumber: position.lineNumber,
|
||||
endLineNumber: position.lineNumber,
|
||||
startColumn: word.startColumn,
|
||||
endColumn: word.endColumn
|
||||
if (schemaLang === 'graphql') {
|
||||
graphqlService ||= initializeMode()
|
||||
graphqlService?.setSchemaConfig([
|
||||
{
|
||||
uri: 'my-schema.graphql',
|
||||
introspectionJSON: schema
|
||||
}
|
||||
|
||||
if (!$dbSchema) {
|
||||
return { suggestions: [] }
|
||||
}
|
||||
|
||||
let suggestions: languages.CompletionItem[] = []
|
||||
|
||||
const noneMatch = textUntilPosition.match(/(?:add|create table)\s/i)
|
||||
|
||||
if (noneMatch) {
|
||||
return {
|
||||
suggestions
|
||||
}
|
||||
}
|
||||
|
||||
for (const schemaKey in $dbSchema) {
|
||||
suggestions.push({
|
||||
label: schemaKey,
|
||||
detail: 'schema',
|
||||
kind: languages.CompletionItemKind.Function,
|
||||
insertText: schemaKey,
|
||||
range: range,
|
||||
sortText: 'z'
|
||||
])
|
||||
} else if (schemaLang === 'mysql' || schemaLang === 'postgresql') {
|
||||
if (sqlSchemaCompletor) {
|
||||
sqlSchemaCompletor.dispose()
|
||||
}
|
||||
sqlSchemaCompletor = languages.registerCompletionItemProvider('sql', {
|
||||
triggerCharacters: ['.', ' ', '('],
|
||||
provideCompletionItems: function (model, position) {
|
||||
const textUntilPosition = model.getValueInRange({
|
||||
startLineNumber: 1,
|
||||
startColumn: 1,
|
||||
endLineNumber: position.lineNumber,
|
||||
endColumn: position.column
|
||||
})
|
||||
|
||||
for (const tableKey in $dbSchema[schemaKey]) {
|
||||
const word = model.getWordUntilPosition(position)
|
||||
const range = {
|
||||
startLineNumber: position.lineNumber,
|
||||
endLineNumber: position.lineNumber,
|
||||
startColumn: word.startColumn,
|
||||
endColumn: word.endColumn
|
||||
}
|
||||
|
||||
let suggestions: languages.CompletionItem[] = []
|
||||
|
||||
const noneMatch = textUntilPosition.match(/(?:add|create table)\s/i)
|
||||
|
||||
if (noneMatch) {
|
||||
return {
|
||||
suggestions
|
||||
}
|
||||
}
|
||||
|
||||
for (const schemaKey in schema) {
|
||||
suggestions.push({
|
||||
label: tableKey,
|
||||
detail: `table (${schemaKey})`,
|
||||
label: schemaKey,
|
||||
detail: 'schema',
|
||||
kind: languages.CompletionItemKind.Function,
|
||||
insertText: tableKey,
|
||||
insertText: schemaKey,
|
||||
range: range,
|
||||
sortText: 'y'
|
||||
sortText: 'z'
|
||||
})
|
||||
|
||||
const noColsMatch = textUntilPosition.match(
|
||||
/(?:from|insert into|update|table)\s(?![\s\S]*(\b(where|order by|group by|values|set|column)\b|\())/i
|
||||
)
|
||||
for (const tableKey in schema[schemaKey]) {
|
||||
suggestions.push({
|
||||
label: tableKey,
|
||||
detail: `table (${schemaKey})`,
|
||||
kind: languages.CompletionItemKind.Function,
|
||||
insertText: tableKey,
|
||||
range: range,
|
||||
sortText: 'y'
|
||||
})
|
||||
|
||||
if (!noColsMatch) {
|
||||
for (const columnKey in $dbSchema[schemaKey][tableKey]) {
|
||||
suggestions.push({
|
||||
label: columnKey,
|
||||
detail: `${$dbSchema[schemaKey][tableKey][columnKey]['type']} (${schemaKey}.${tableKey})`,
|
||||
kind: languages.CompletionItemKind.Function,
|
||||
insertText: columnKey,
|
||||
range: range,
|
||||
sortText: 'x'
|
||||
})
|
||||
const noColsMatch = textUntilPosition.match(
|
||||
/(?:from|insert into|update|table)\s(?![\s\S]*(\b(where|order by|group by|values|set|column)\b|\())/i
|
||||
)
|
||||
|
||||
if (!noColsMatch) {
|
||||
for (const columnKey in schema[schemaKey][tableKey]) {
|
||||
suggestions.push({
|
||||
label: columnKey,
|
||||
detail: `${schema[schemaKey][tableKey][columnKey]['type']} (${schemaKey}.${tableKey})`,
|
||||
kind: languages.CompletionItemKind.Function,
|
||||
insertText: columnKey,
|
||||
range: range,
|
||||
sortText: 'x'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (textUntilPosition.match(new RegExp(`${tableKey}.$`, 'i'))) {
|
||||
suggestions = suggestions.filter((x) =>
|
||||
x.detail?.includes(`(${schemaKey}.${tableKey})`)
|
||||
)
|
||||
return {
|
||||
suggestions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (textUntilPosition.match(new RegExp(`${tableKey}.$`, 'i'))) {
|
||||
suggestions = suggestions.filter((x) =>
|
||||
x.detail?.includes(`(${schemaKey}.${tableKey})`)
|
||||
)
|
||||
if (textUntilPosition.match(new RegExp(`${schemaKey}.$`, 'i'))) {
|
||||
suggestions = suggestions.filter((x) => x.detail === `table (${schemaKey})`)
|
||||
return {
|
||||
suggestions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (textUntilPosition.match(new RegExp(`${schemaKey}.$`, 'i'))) {
|
||||
suggestions = suggestions.filter((x) => x.detail === `table (${schemaKey})`)
|
||||
return {
|
||||
suggestions
|
||||
}
|
||||
return {
|
||||
suggestions
|
||||
}
|
||||
}
|
||||
return {
|
||||
suggestions
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const outputChannel = {
|
||||
@@ -815,6 +830,7 @@
|
||||
onDestroy(() => {
|
||||
disposeMethod && disposeMethod()
|
||||
websocketInterval && clearInterval(websocketInterval)
|
||||
sqlSchemaCompletor && sqlSchemaCompletor.dispose()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<script lang="ts">
|
||||
import { BROWSER } from 'esm-env'
|
||||
|
||||
import 'monaco-editor/esm/vs/editor/edcore.main'
|
||||
import { editor as meditor } from 'monaco-editor/esm/vs/editor/editor.api'
|
||||
import 'monaco-editor/esm/vs/basic-languages/graphql/graphql.contribution'
|
||||
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
|
||||
let divEl: HTMLDivElement | null = null
|
||||
let editor: meditor.IStandaloneCodeEditor
|
||||
|
||||
export let code: string = ''
|
||||
|
||||
async function loadMonaco() {
|
||||
editor = meditor.create(divEl as HTMLDivElement, {
|
||||
value: code,
|
||||
language: 'graphql',
|
||||
readOnly: true,
|
||||
automaticLayout: true,
|
||||
scrollBeyondLastLine: false,
|
||||
lineNumbers: 'off',
|
||||
minimap: { enabled: false }
|
||||
})
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
if (BROWSER) {
|
||||
await loadMonaco()
|
||||
}
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
try {
|
||||
editor && editor.dispose()
|
||||
} catch (err) {}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div bind:this={divEl} class="{$$props.class ?? ''} editor" />
|
||||
@@ -9,7 +9,7 @@
|
||||
import AppConnect from './AppConnect.svelte'
|
||||
import { Button } from './common'
|
||||
import ResourceEditor from './ResourceEditor.svelte'
|
||||
import DbSchemaExplorer from './DBSchemaExplorer.svelte'
|
||||
import DBSchemaExplorer from './DBSchemaExplorer.svelte'
|
||||
import DarkModeObserver from './DarkModeObserver.svelte'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
@@ -143,5 +143,5 @@
|
||||
<Icon scale={0.8} data={faRotateRight} />
|
||||
</Button>
|
||||
</div>
|
||||
<DbSchemaExplorer {resourceType} resourcePath={value} />
|
||||
<DBSchemaExplorer {resourceType} resourcePath={value} />
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { onDestroy, tick } from 'svelte'
|
||||
import type { Preview } from '$lib/gen/models/Preview'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import type { SupportedLanguage } from '$lib/common'
|
||||
|
||||
export let isLoading = false
|
||||
export let job: { completed: boolean; result: any; id: string } | undefined = undefined
|
||||
@@ -84,7 +85,7 @@
|
||||
export async function runPreview(
|
||||
path: string | undefined,
|
||||
code: string,
|
||||
lang: 'deno' | 'go' | 'python3' | 'bash' | 'nativets',
|
||||
lang: SupportedLanguage,
|
||||
args: Record<string, any>,
|
||||
tag: string | undefined
|
||||
): Promise<string> {
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
langs.push(['MySQL', Script.language.MYSQL])
|
||||
langs.push(['BigQuery', Script.language.BIGQUERY])
|
||||
langs.push(['Snowflake', Script.language.SNOWFLAKE])
|
||||
langs.push(['GraphQL', Script.language.GRAPHQL])
|
||||
if (SCRIPT_SHOW_GO) {
|
||||
langs.push(['Go', Script.language.GO])
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution'
|
||||
import 'monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution'
|
||||
import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution'
|
||||
import 'monaco-editor/esm/vs/basic-languages/graphql/graphql.contribution'
|
||||
import 'monaco-editor/esm/vs/language/json/monaco.contribution'
|
||||
import 'monaco-editor/esm/vs/language/typescript/monaco.contribution'
|
||||
|
||||
|
||||
@@ -53,6 +53,10 @@ export async function main(database: any) {
|
||||
snowflake: {
|
||||
code: `select 1`,
|
||||
lang: 'snowflake'
|
||||
},
|
||||
graphql: {
|
||||
code: '{ __typename }',
|
||||
lang: 'graphql'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
['mysql', 'MySQL'],
|
||||
['bigquery', 'BigQuery'],
|
||||
['snowflake', 'Snowflake'],
|
||||
['graphql', 'GraphQL'],
|
||||
['bun', 'TypeScript (Bun)']
|
||||
] as [Script.language, string][]
|
||||
</script>
|
||||
|
||||
@@ -74,6 +74,13 @@ export function buildWorkerDefinition(
|
||||
return buildWorker(workerOverrideGlobals, label, 'cssWorker', 'CSS Worker')
|
||||
case 'json':
|
||||
return buildWorker(workerOverrideGlobals, label, 'jsonWorker', 'JSON Worker')
|
||||
case 'graphql':
|
||||
const workerFilename = `graphql.worker.bundle.js`
|
||||
const workerPathLocal = `${workerOverrideGlobals.workerPath}/${workerFilename}`
|
||||
const workerUrl = new URL(workerPathLocal, workerOverrideGlobals.basePath)
|
||||
return new Worker(workerUrl.href, {
|
||||
name: label
|
||||
})
|
||||
default:
|
||||
return buildWorker(workerOverrideGlobals, label, 'editorWorker', 'Editor Worker')
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import type Editor from '../Editor.svelte'
|
||||
import { faCheck, faClose, faMagicWandSparkles } from '@fortawesome/free-solid-svg-icons'
|
||||
import { dbSchema, dbSchemaPublicOnly, existsOpenaiResourcePath } from '$lib/stores'
|
||||
import { dbSchema, existsOpenaiResourcePath } from '$lib/stores'
|
||||
import type DiffEditor from '../DiffEditor.svelte'
|
||||
import { scriptLangToEditorLang } from '$lib/scripts'
|
||||
import Popover from '../Popover.svelte'
|
||||
@@ -38,8 +38,7 @@
|
||||
language: lang,
|
||||
code: editor?.getCode() || '',
|
||||
error,
|
||||
dbSchema: $dbSchema,
|
||||
dbSchemaPublicOnly: $dbSchemaPublicOnly
|
||||
dbSchema: $dbSchema
|
||||
})
|
||||
generatedCode = result.code
|
||||
explanation = result.explanation
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { faCheck, faClose, faMagicWandSparkles } from '@fortawesome/free-solid-svg-icons'
|
||||
import Popup from '../common/popup/Popup.svelte'
|
||||
import { Icon } from 'svelte-awesome'
|
||||
import { dbSchema, dbSchemaPublicOnly, existsOpenaiResourcePath } from '$lib/stores'
|
||||
import { dbSchema, existsOpenaiResourcePath } from '$lib/stores'
|
||||
import type DiffEditor from '../DiffEditor.svelte'
|
||||
import { scriptLangToEditorLang } from '$lib/scripts'
|
||||
import type { Selection } from 'monaco-editor/esm/vs/editor/editor.api'
|
||||
@@ -46,16 +46,14 @@
|
||||
language: lang,
|
||||
description: funcDesc,
|
||||
selectedCode,
|
||||
dbSchema: $dbSchema,
|
||||
dbSchemaPublicOnly: $dbSchemaPublicOnly
|
||||
dbSchema: $dbSchema
|
||||
})
|
||||
generatedCode = originalCode.replace(selectedCode, result.code + '\n')
|
||||
} else {
|
||||
const result = await generateScript({
|
||||
language: lang,
|
||||
description: funcDesc,
|
||||
dbSchema: $dbSchema,
|
||||
dbSchemaPublicOnly: $dbSchemaPublicOnly
|
||||
dbSchema: $dbSchema
|
||||
})
|
||||
generatedCode = result.code
|
||||
}
|
||||
@@ -229,8 +227,8 @@
|
||||
In order to better generate the script, we pass the selected DB schema to GPT-4.
|
||||
</Tooltip>
|
||||
</p>
|
||||
{#if lang === 'postgresql'}
|
||||
<ToggleButtonGroup class="w-auto shrink-0" bind:selected={$dbSchemaPublicOnly}>
|
||||
{#if $dbSchema.lang === 'postgresql'}
|
||||
<ToggleButtonGroup class="w-auto shrink-0" bind:selected={$dbSchema.publicOnly}>
|
||||
<ToggleButton value={true} label="Public schema" />
|
||||
<ToggleButton value={false} label="All schemas" />
|
||||
</ToggleButtonGroup>
|
||||
|
||||
@@ -41,7 +41,6 @@ workspaceStore.subscribe(async (value) => {
|
||||
interface BaseOptions {
|
||||
language: Script.language | 'frontend'
|
||||
dbSchema: DBSchema | undefined
|
||||
dbSchemaPublicOnly: boolean
|
||||
}
|
||||
|
||||
interface ScriptGenerationOptions extends BaseOptions {
|
||||
@@ -71,19 +70,24 @@ async function addResourceTypes(scriptOptions: BaseOptions, workspace: string, p
|
||||
}
|
||||
|
||||
function addDBSChema(scriptOptions: BaseOptions, prompt: string) {
|
||||
if (['mysql', 'postgresql'].includes(scriptOptions.language) && scriptOptions.dbSchema) {
|
||||
const { dbSchema, dbSchemaPublicOnly, language } = scriptOptions
|
||||
const { dbSchema, language } = scriptOptions
|
||||
if (
|
||||
dbSchema &&
|
||||
['postgresql', 'mysql'].includes(language) && // make sure we are using a SQL language
|
||||
(dbSchema.lang === 'postgresql' || dbSchema.lang === 'mysql') // make sure we have a SQL schema
|
||||
) {
|
||||
const { schema, lang } = dbSchema
|
||||
let smallerSchema: {
|
||||
[schemaKey: string]: {
|
||||
[tableKey: string]: Array<[string, string, boolean, string?]>
|
||||
}
|
||||
} = {}
|
||||
for (const schemaKey in dbSchema) {
|
||||
for (const schemaKey in schema) {
|
||||
smallerSchema[schemaKey] = {}
|
||||
for (const tableKey in dbSchema[schemaKey]) {
|
||||
for (const tableKey in schema[schemaKey]) {
|
||||
smallerSchema[schemaKey][tableKey] = []
|
||||
for (const colKey in dbSchema[schemaKey][tableKey]) {
|
||||
const col = dbSchema[schemaKey][tableKey][colKey]
|
||||
for (const colKey in schema[schemaKey][tableKey]) {
|
||||
const col = schema[schemaKey][tableKey][colKey]
|
||||
const p: [string, string, boolean, string?] = [colKey, col.type, col.required]
|
||||
if (col.default) {
|
||||
p.push(col.default)
|
||||
@@ -98,9 +102,9 @@ function addDBSChema(scriptOptions: BaseOptions, prompt: string) {
|
||||
| {
|
||||
[tableKey: string]: Array<[string, string, boolean, string?]>
|
||||
} = smallerSchema
|
||||
if (language === 'postgresql' && dbSchemaPublicOnly) {
|
||||
if (lang === 'postgresql' && dbSchema.publicOnly) {
|
||||
finalSchema = smallerSchema.public || smallerSchema
|
||||
} else if (language === 'mysql' && Object.keys(smallerSchema).length === 1) {
|
||||
} else if (lang === 'mysql' && Object.keys(smallerSchema).length === 1) {
|
||||
finalSchema = smallerSchema[Object.keys(smallerSchema)[0]]
|
||||
}
|
||||
prompt =
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
import PowershellIcon from '$lib/components/icons/PowershellIcon.svelte'
|
||||
import BigQueryIcon from '$lib/components/icons/BigQueryIcon.svelte'
|
||||
import SnowflakeIcon from '$lib/components/icons/SnowflakeIcon.svelte'
|
||||
import GraphqlIcon from '$lib/components/icons/GraphqlIcon.svelte'
|
||||
|
||||
export let lang:
|
||||
| SupportedLanguage
|
||||
@@ -32,8 +33,10 @@
|
||||
[Script.language.GO]: 'Go',
|
||||
[Script.language.BASH]: 'Bash',
|
||||
[Script.language.NATIVETS]: 'HTTP',
|
||||
// [Script.language.GRAPHQL]: 'HTTP',
|
||||
[Script.language.POSTGRESQL]: 'Postgresql'
|
||||
[Script.language.GRAPHQL]: 'GraphQL',
|
||||
[Script.language.POSTGRESQL]: 'Postgresql',
|
||||
[Script.language.BIGQUERY]: 'BigQuery',
|
||||
[Script.language.SNOWFLAKE]: 'Snowflake'
|
||||
}
|
||||
|
||||
const langToComponent: Record<
|
||||
@@ -56,7 +59,7 @@
|
||||
powershell: PowershellIcon,
|
||||
postgresql: PostgresIcon,
|
||||
nativets: RestIcon,
|
||||
graphql: RestIcon
|
||||
graphql: GraphqlIcon
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -188,6 +188,18 @@
|
||||
}}
|
||||
/>
|
||||
|
||||
<FlowScriptPicker
|
||||
label="GraphQL"
|
||||
lang={Script.language.GRAPHQL}
|
||||
on:click={() => {
|
||||
dispatch('new', {
|
||||
language: RawScript.language.GRAPHQL,
|
||||
kind,
|
||||
subkind: 'flow'
|
||||
})
|
||||
}}
|
||||
/>
|
||||
|
||||
<FlowScriptPicker
|
||||
label={`Docker`}
|
||||
lang="docker"
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
<script lang="ts">
|
||||
export let height = '24px'
|
||||
export let width = '24px'
|
||||
</script>
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="GraphQL_Logo"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 400 400"
|
||||
enable-background="new 0 0 400 400"
|
||||
xml:space="preserve"
|
||||
{width}
|
||||
{height}
|
||||
>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="122"
|
||||
y="-0.4"
|
||||
transform="matrix(-0.866 -0.5 0.5 -0.866 163.3196 363.3136)"
|
||||
fill="#E535AB"
|
||||
width="16.6"
|
||||
height="320.3"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="39.8" y="272.2" fill="#E535AB" width="320.3" height="16.6" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="37.9"
|
||||
y="312.2"
|
||||
transform="matrix(-0.866 -0.5 0.5 -0.866 83.0693 663.3409)"
|
||||
fill="#E535AB"
|
||||
width="185"
|
||||
height="16.6"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="177.1"
|
||||
y="71.1"
|
||||
transform="matrix(-0.866 -0.5 0.5 -0.866 463.3409 283.0693)"
|
||||
fill="#E535AB"
|
||||
width="185"
|
||||
height="16.6"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="122.1"
|
||||
y="-13"
|
||||
transform="matrix(-0.5 -0.866 0.866 -0.5 126.7903 232.1221)"
|
||||
fill="#E535AB"
|
||||
width="16.6"
|
||||
height="185"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="109.6"
|
||||
y="151.6"
|
||||
transform="matrix(-0.5 -0.866 0.866 -0.5 266.0828 473.3766)"
|
||||
fill="#E535AB"
|
||||
width="320.3"
|
||||
height="16.6"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="52.5" y="107.5" fill="#E535AB" width="16.6" height="185" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect x="330.9" y="107.5" fill="#E535AB" width="16.6" height="185" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<rect
|
||||
x="262.4"
|
||||
y="240.1"
|
||||
transform="matrix(-0.5 -0.866 0.866 -0.5 126.7953 714.2875)"
|
||||
fill="#E535AB"
|
||||
width="14.5"
|
||||
height="160.9"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M369.5,297.9c-9.6,16.7-31,22.4-47.7,12.8c-16.7-9.6-22.4-31-12.8-47.7c9.6-16.7,31-22.4,47.7-12.8
|
||||
C373.5,259.9,379.2,281.2,369.5,297.9"
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M90.9,137c-9.6,16.7-31,22.4-47.7,12.8c-16.7-9.6-22.4-31-12.8-47.7c9.6-16.7,31-22.4,47.7-12.8
|
||||
C94.8,99,100.5,120.3,90.9,137"
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M30.5,297.9c-9.6-16.7-3.9-38,12.8-47.7c16.7-9.6,38-3.9,47.7,12.8c9.6,16.7,3.9,38-12.8,47.7
|
||||
C61.4,320.3,40.1,314.6,30.5,297.9"
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M309.1,137c-9.6-16.7-3.9-38,12.8-47.7c16.7-9.6,38-3.9,47.7,12.8c9.6,16.7,3.9,38-12.8,47.7
|
||||
C340.1,159.4,318.7,153.7,309.1,137"
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M200,395.8c-19.3,0-34.9-15.6-34.9-34.9c0-19.3,15.6-34.9,34.9-34.9c19.3,0,34.9,15.6,34.9,34.9
|
||||
C234.9,380.1,219.3,395.8,200,395.8"
|
||||
/>
|
||||
<path
|
||||
fill="#E535AB"
|
||||
d="M200,74c-19.3,0-34.9-15.6-34.9-34.9c0-19.3,15.6-34.9,34.9-34.9c19.3,0,34.9,15.6,34.9,34.9
|
||||
C234.9,58.4,219.3,74,200,74"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
@@ -63,6 +63,8 @@ export function langToExt(lang: string): string {
|
||||
return 'ts'
|
||||
case 'nativets':
|
||||
return 'ts'
|
||||
case 'graphql':
|
||||
return 'gql'
|
||||
|
||||
default:
|
||||
return 'unknown'
|
||||
|
||||
@@ -11,7 +11,8 @@ import init, {
|
||||
parse_sql,
|
||||
parse_mysql,
|
||||
parse_bigquery,
|
||||
parse_snowflake
|
||||
parse_snowflake,
|
||||
parse_graphql
|
||||
} from 'windmill-parser-wasm'
|
||||
import wasmUrl from 'windmill-parser-wasm/windmill_parser_wasm_bg.wasm?url'
|
||||
import { workspaceStore } from './stores.js'
|
||||
@@ -63,6 +64,12 @@ export async function inferArgs(
|
||||
{ name: 'database', typ: { resource: 'snowflake' } },
|
||||
...inferedSchema.args
|
||||
]
|
||||
} else if (language == 'graphql') {
|
||||
inferedSchema = JSON.parse(parse_graphql(code))
|
||||
inferedSchema.args = [
|
||||
{ name: 'database', typ: { resource: 'graphql' } },
|
||||
...inferedSchema.args
|
||||
]
|
||||
} else if (language == 'go') {
|
||||
inferedSchema = JSON.parse(parse_go(code))
|
||||
} else if (language == 'bash') {
|
||||
|
||||
@@ -138,7 +138,8 @@ INSERT INTO demo VALUES (?, ?)
|
||||
|
||||
export const BIGQUERY_INIT_CODE = `-- @name1 (string) = default arg
|
||||
-- @name2 (integer)
|
||||
INSERT INTO \`demodb.demo\` VALUES (@name1, @name2)
|
||||
-- @name3 (string[])
|
||||
INSERT INTO \`demodb.demo\` VALUES (@name1, @name2, @name3)
|
||||
`
|
||||
|
||||
export const SNOWFLAKE_INIT_CODE = `-- ? name1 (varchar) = default arg
|
||||
@@ -146,6 +147,15 @@ export const SNOWFLAKE_INIT_CODE = `-- ? name1 (varchar) = default arg
|
||||
INSERT INTO demo VALUES (?, ?)
|
||||
`
|
||||
|
||||
export const GRAPHQL_INIT_CODE = `query($name1: String, $name2: Int, $name3: [String]) {
|
||||
demo(name1: $name1, name2: $name2, name3: $name3) {
|
||||
name1,
|
||||
name2,
|
||||
name3
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const FETCH_INIT_CODE = `export async function main(
|
||||
url: string | undefined,
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' = 'GET',
|
||||
@@ -270,6 +280,7 @@ const ALL_INITIAL_CODE = [
|
||||
MYSQL_INIT_CODE,
|
||||
BIGQUERY_INIT_CODE,
|
||||
SNOWFLAKE_INIT_CODE,
|
||||
GRAPHQL_INIT_CODE,
|
||||
DENO_INIT_CODE_TRIGGER,
|
||||
DENO_INIT_CODE_CLEAR,
|
||||
PYTHON_INIT_CODE_CLEAR,
|
||||
@@ -344,6 +355,8 @@ export function initialCode(
|
||||
return BIGQUERY_INIT_CODE
|
||||
} else if (language == 'snowflake') {
|
||||
return SNOWFLAKE_INIT_CODE
|
||||
} else if (language == 'graphql') {
|
||||
return GRAPHQL_INIT_CODE
|
||||
} else if (language == 'bun') {
|
||||
if (subkind === 'flow') {
|
||||
return BUN_INIT_CODE_CLEAR
|
||||
|
||||
@@ -18,7 +18,7 @@ export function scriptLangToEditorLang(lang: Script.language) {
|
||||
return 'sql'
|
||||
} else if (lang == 'bigquery') {
|
||||
return 'sql'
|
||||
} else if (lang == "snowflake") {
|
||||
} else if (lang == 'snowflake') {
|
||||
return 'sql'
|
||||
} else if (lang == 'python3') {
|
||||
return 'python'
|
||||
|
||||
@@ -2,6 +2,7 @@ import { BROWSER } from 'esm-env'
|
||||
import { derived, type Readable, writable } from 'svelte/store'
|
||||
import type { UserWorkspaceList } from '$lib/gen/models/UserWorkspaceList.js'
|
||||
import type { TokenResponse } from './gen'
|
||||
import type { IntrospectionQuery } from 'graphql'
|
||||
|
||||
export interface UserExt {
|
||||
email: string
|
||||
@@ -66,7 +67,8 @@ export const hubScripts = writable<
|
||||
| undefined
|
||||
>(undefined)
|
||||
export const existsOpenaiResourcePath = writable<boolean>(false)
|
||||
export type DBSchema = {
|
||||
|
||||
type SQLBaseSchema = {
|
||||
[schemaKey: string]: {
|
||||
[tableKey: string]: {
|
||||
[columnKey: string]: {
|
||||
@@ -78,9 +80,25 @@ export type DBSchema = {
|
||||
}
|
||||
}
|
||||
|
||||
export const dbSchema = writable<DBSchema | undefined>(undefined)
|
||||
export interface MysqlSchema {
|
||||
lang: 'mysql'
|
||||
schema: SQLBaseSchema
|
||||
}
|
||||
|
||||
export const dbSchemaPublicOnly = writable<boolean>(true)
|
||||
export interface PostgresqlSchema {
|
||||
lang: 'postgresql'
|
||||
schema: SQLBaseSchema
|
||||
publicOnly: boolean
|
||||
}
|
||||
|
||||
export interface GraphqlSchema {
|
||||
lang: 'graphql'
|
||||
schema: IntrospectionQuery
|
||||
}
|
||||
|
||||
export type DBSchema = MysqlSchema | PostgresqlSchema | GraphqlSchema
|
||||
|
||||
export const dbSchema = writable<DBSchema | undefined>(undefined)
|
||||
|
||||
export function switchWorkspace(workspace: string | undefined) {
|
||||
localStorage.removeItem('flow')
|
||||
|
||||
@@ -291,7 +291,7 @@
|
||||
<Badge color="blue">{job.job_kind}</Badge>
|
||||
</div>
|
||||
{/if}
|
||||
{#if job.tag && !['deno', 'python3', 'flow', 'other', 'go', 'postgresql', 'mysql', 'bigquery', 'snowflake', 'nativets', 'bash', 'other', 'dependency'].includes(job.tag)}
|
||||
{#if job.tag && !['deno', 'python3', 'flow', 'other', 'go', 'postgresql', 'mysql', 'bigquery', 'snowflake', 'graphql', 'nativets', 'bash', 'other', 'dependency'].includes(job.tag)}
|
||||
<div>
|
||||
<Badge color="indigo">Worker group: {job.tag}</Badge>
|
||||
</div>
|
||||
|
||||
+15
-1
@@ -2,6 +2,7 @@ import { sveltekit } from '@sveltejs/kit/vite'
|
||||
import { readFileSync } from 'fs'
|
||||
import { fileURLToPath } from 'url'
|
||||
import ViteYaml from '@modyfi/vite-plugin-yaml'
|
||||
import monacoEditorPlugin from 'vite-plugin-monaco-editor'
|
||||
|
||||
const file = fileURLToPath(new URL('package.json', import.meta.url))
|
||||
const json = readFileSync(file, 'utf8')
|
||||
@@ -32,7 +33,20 @@ const config = {
|
||||
preview: {
|
||||
port: 3000
|
||||
},
|
||||
plugins: [sveltekit(), ViteYaml()],
|
||||
plugins: [
|
||||
sveltekit(),
|
||||
ViteYaml(),
|
||||
monacoEditorPlugin.default({
|
||||
publicPath: 'workers',
|
||||
languageWorkers: [],
|
||||
customWorkers: [
|
||||
{
|
||||
label: 'graphql',
|
||||
entry: 'monaco-graphql/esm/graphql.worker'
|
||||
}
|
||||
]
|
||||
})
|
||||
],
|
||||
define: {
|
||||
__pkg__: version
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user