diff --git a/backend/Cargo.lock b/backend/Cargo.lock
index dfee10ac35..125e1a2647 100644
--- a/backend/Cargo.lock
+++ b/backend/Cargo.lock
@@ -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",
diff --git a/backend/Cargo.toml b/backend/Cargo.toml
index 3dbdd5f6d1..a45a6c3165 100644
--- a/backend/Cargo.toml
+++ b/backend/Cargo.toml
@@ -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"
diff --git a/backend/migrations/20230807132313_add_graphql_lang.down.sql b/backend/migrations/20230807132313_add_graphql_lang.down.sql
new file mode 100644
index 0000000000..d2f607c5b8
--- /dev/null
+++ b/backend/migrations/20230807132313_add_graphql_lang.down.sql
@@ -0,0 +1 @@
+-- Add down migration script here
diff --git a/backend/migrations/20230807132313_add_graphql_lang.up.sql b/backend/migrations/20230807132313_add_graphql_lang.up.sql
new file mode 100644
index 0000000000..c6ee654574
--- /dev/null
+++ b/backend/migrations/20230807132313_add_graphql_lang.up.sql
@@ -0,0 +1,2 @@
+-- Add up migration script here
+ALTER TYPE SCRIPT_LANG ADD VALUE IF NOT EXISTS 'graphql';
\ No newline at end of file
diff --git a/backend/parsers/windmill-parser-graphql/Cargo.toml b/backend/parsers/windmill-parser-graphql/Cargo.toml
new file mode 100644
index 0000000000..da1a8bf9c6
--- /dev/null
+++ b/backend/parsers/windmill-parser-graphql/Cargo.toml
@@ -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
\ No newline at end of file
diff --git a/backend/parsers/windmill-parser-graphql/src/lib.rs b/backend/parsers/windmill-parser-graphql/src/lib.rs
new file mode 100644
index 0000000000..365a4e2074
--- /dev/null
+++ b/backend/parsers/windmill-parser-graphql/src/lib.rs
@@ -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 {
+ 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
- {#if lang === 'postgresql'}
-
+ {#if $dbSchema.lang === 'postgresql'}
+
diff --git a/frontend/src/lib/components/codeGen/lib.ts b/frontend/src/lib/components/codeGen/lib.ts
index e4a5631855..86da0f5d34 100644
--- a/frontend/src/lib/components/codeGen/lib.ts
+++ b/frontend/src/lib/components/codeGen/lib.ts
@@ -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 =
diff --git a/frontend/src/lib/components/common/languageIcons/LanguageIcon.svelte b/frontend/src/lib/components/common/languageIcons/LanguageIcon.svelte
index 89b40b255c..b41eec947e 100644
--- a/frontend/src/lib/components/common/languageIcons/LanguageIcon.svelte
+++ b/frontend/src/lib/components/common/languageIcons/LanguageIcon.svelte
@@ -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
}
diff --git a/frontend/src/lib/components/flows/content/FlowInputs.svelte b/frontend/src/lib/components/flows/content/FlowInputs.svelte
index 129481d0ef..a4903a2801 100644
--- a/frontend/src/lib/components/flows/content/FlowInputs.svelte
+++ b/frontend/src/lib/components/flows/content/FlowInputs.svelte
@@ -188,6 +188,18 @@
}}
/>
+ {
+ dispatch('new', {
+ language: RawScript.language.GRAPHQL,
+ kind,
+ subkind: 'flow'
+ })
+ }}
+ />
+
+ export let height = '24px'
+ export let width = '24px'
+
+
+
diff --git a/frontend/src/lib/editorUtils.ts b/frontend/src/lib/editorUtils.ts
index 0e4f945863..fb0a9cd24d 100644
--- a/frontend/src/lib/editorUtils.ts
+++ b/frontend/src/lib/editorUtils.ts
@@ -63,6 +63,8 @@ export function langToExt(lang: string): string {
return 'ts'
case 'nativets':
return 'ts'
+ case 'graphql':
+ return 'gql'
default:
return 'unknown'
diff --git a/frontend/src/lib/infer.ts b/frontend/src/lib/infer.ts
index 882d7d0805..41396650ea 100644
--- a/frontend/src/lib/infer.ts
+++ b/frontend/src/lib/infer.ts
@@ -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') {
diff --git a/frontend/src/lib/script_helpers.ts b/frontend/src/lib/script_helpers.ts
index a73fd0cc19..f39660349e 100644
--- a/frontend/src/lib/script_helpers.ts
+++ b/frontend/src/lib/script_helpers.ts
@@ -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
diff --git a/frontend/src/lib/scripts.ts b/frontend/src/lib/scripts.ts
index 766827b63c..4c5cbe33a5 100644
--- a/frontend/src/lib/scripts.ts
+++ b/frontend/src/lib/scripts.ts
@@ -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'
diff --git a/frontend/src/lib/stores.ts b/frontend/src/lib/stores.ts
index e0de369418..0b4d3e5656 100644
--- a/frontend/src/lib/stores.ts
+++ b/frontend/src/lib/stores.ts
@@ -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(false)
-export type DBSchema = {
+
+type SQLBaseSchema = {
[schemaKey: string]: {
[tableKey: string]: {
[columnKey: string]: {
@@ -78,9 +80,25 @@ export type DBSchema = {
}
}
-export const dbSchema = writable(undefined)
+export interface MysqlSchema {
+ lang: 'mysql'
+ schema: SQLBaseSchema
+}
-export const dbSchemaPublicOnly = writable(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(undefined)
export function switchWorkspace(workspace: string | undefined) {
localStorage.removeItem('flow')
diff --git a/frontend/src/routes/(root)/(logged)/run/[...run]/+page.svelte b/frontend/src/routes/(root)/(logged)/run/[...run]/+page.svelte
index a7f1df2b2d..c0c3010344 100644
--- a/frontend/src/routes/(root)/(logged)/run/[...run]/+page.svelte
+++ b/frontend/src/routes/(root)/(logged)/run/[...run]/+page.svelte
@@ -291,7 +291,7 @@
{job.job_kind}
{/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)}
Worker group: {job.tag}
diff --git a/frontend/vite.config.js b/frontend/vite.config.js
index d553b1f168..41cc6443df 100644
--- a/frontend/vite.config.js
+++ b/frontend/vite.config.js
@@ -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
},