From 34abe01c2f0d9d7055ed17cb2dd34acf9d5e2e88 Mon Sep 17 00:00:00 2001 From: HugoCasa Date: Wed, 15 Nov 2023 18:44:16 +0100 Subject: [PATCH] feat: add yaml for import/export/diff (#2631) * feat: add yaml for import/export/diff * fix: speeeeeeed --- frontend/package-lock.json | 56 ++++++++++++------ frontend/package.json | 8 ++- frontend/src/lib/components/DiffDrawer.svelte | 11 +++- frontend/src/lib/components/FlowViewer.svelte | 57 ++++++++++++------- .../apps/editor/AppEditorHeader.svelte | 6 +- .../apps/editor/AppExportButton.svelte | 55 +++++++++++++----- .../details/DetailPageDetailPanel.svelte | 50 +++++++++++----- .../components/flows/CreateActionsApp.svelte | 35 +++++++++--- .../components/flows/CreateActionsFlow.svelte | 34 ++++++++--- .../flows/header/FlowImportExportMenu.svelte | 6 +- frontend/src/lib/utils.ts | 7 +++ .../(root)/(logged)/apps/add/+page.svelte | 16 +++--- .../(root)/(logged)/flows/add/+page.svelte | 2 +- 13 files changed, 243 insertions(+), 100 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 925f0428f4..f34406e23d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -27,6 +27,7 @@ "driver.js": "^1.3.0", "esm-env": "^1.0.0", "fast-equals": "^5.0.1", + "fsevents": "*", "graphql": "^16.7.1", "highlight.js": "^11.8.0", "lodash": "^4.17.21", @@ -52,6 +53,7 @@ "windmill-parser-wasm": "^1.202.1", "y-monaco": "^0.1.4", "y-websocket": "^1.5.0", + "yaml": "^2.3.4", "yjs": "^13.6.7" }, "devDependencies": { @@ -109,6 +111,9 @@ "vite-plugin-monaco-editor": "^1.1.0", "yootils": "^0.3.1" }, + "optionalDependencies": { + "fsevents": "^2.3.3" + }, "peerDependencies": { "@sveltejs/kit": "^1.25.0", "svelte": "^4.0.0" @@ -1198,6 +1203,20 @@ "fsevents": "2.3.2" } }, + "node_modules/@playwright/test/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -4332,10 +4351,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, "os": [ @@ -10226,10 +10244,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", - "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", - "dev": true, + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", "engines": { "node": ">= 14" } @@ -11004,6 +11021,15 @@ "@types/node": "*", "fsevents": "2.3.2", "playwright-core": "1.34.3" + }, + "dependencies": { + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + } } }, "@polka/url": { @@ -13295,10 +13321,9 @@ "dev": true }, "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "optional": true }, "function-bind": { @@ -17372,10 +17397,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yaml": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", - "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", - "dev": true + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==" }, "yargs-parser": { "version": "20.2.9", diff --git a/frontend/package.json b/frontend/package.json index f10511c15b..80b3b27466 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -39,6 +39,7 @@ "eslint": "^8.47.0", "eslint-config-prettier": "^8.6.0", "eslint-plugin-svelte": "^2.33.1", + "monaco-editor-workers": "~0.44.0", "ol": "^7.4.0", "openapi-typescript-codegen": "^0.25.0", "path-browserify": "^1.0.1", @@ -68,8 +69,7 @@ "vite": "^4.5.0", "vite-plugin-circular-dependency": "^0.2.1", "vite-plugin-monaco-editor": "^1.1.0", - "yootils": "^0.3.1", - "monaco-editor-workers": "~0.44.0" + "yootils": "^0.3.1" }, "overrides": { "@rgossiaux/svelte-headlessui": { @@ -136,6 +136,7 @@ "windmill-parser-wasm": "^1.202.1", "y-monaco": "^0.1.4", "y-websocket": "^1.5.0", + "yaml": "^2.3.4", "yjs": "^13.6.7" }, "peerDependencies": { @@ -390,5 +391,8 @@ "./package/gen/core/OpenAPI.d.ts" ] } + }, + "optionalDependencies": { + "fsevents": "^2.3.3" } } diff --git a/frontend/src/lib/components/DiffDrawer.svelte b/frontend/src/lib/components/DiffDrawer.svelte index 0cdaaf3c8d..ac01e49882 100644 --- a/frontend/src/lib/components/DiffDrawer.svelte +++ b/frontend/src/lib/components/DiffDrawer.svelte @@ -6,7 +6,12 @@ import Tabs from './common/tabs/Tabs.svelte' import Tab from './common/tabs/Tab.svelte' import { cloneDeep } from 'lodash' - import { cleanValueProperties, orderedJsonStringify, type Value } from '$lib/utils' + import { + cleanValueProperties, + orderedJsonStringify, + orderedYamlStringify, + type Value + } from '$lib/utils' import type { Script } from '$lib/gen' type DiffData = { @@ -60,7 +65,7 @@ return { lang: data.language ? scriptLangToEditorLang(data.language as Script.language) : undefined, content, - metadata: orderedJsonStringify(metadata, 2) + metadata: orderedYamlStringify(metadata) } } @@ -217,7 +222,7 @@ (SVELVET_CONTEXT_KEY, { fullHeight: true @@ -36,9 +38,11 @@ export let noGraph: boolean = false - export let tab: 'ui' | 'json' | 'schema' = noGraph ? 'schema' : 'ui' + export let tab: 'ui' | 'raw' | 'schema' = noGraph ? 'schema' : 'ui' export let noSummary = false + let rawType: 'json' | 'yaml' = 'yaml' + let open: { [id: number]: boolean } = {} if (initialOpen) { open[initialOpen] = true @@ -53,7 +57,7 @@ {#if !noGraph} Graph {/if} - JSON + Raw Input Schema @@ -91,24 +95,37 @@ - -
- - -
+ + YAML + JSON + +
+ + +
+
+
diff --git a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte index 22381705a9..9ca7cb2e0b 100644 --- a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte +++ b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte @@ -47,8 +47,8 @@ classNames, cleanValueProperties, copyToClipboard, - orderedJsonStringify, - truncateRev + truncateRev, + orderedJsonStringify } from '../../../utils' import type { AppInput, @@ -484,7 +484,7 @@ } }, { - displayName: 'JSON', + displayName: 'Export', icon: FileJson, action: () => { appExport.open($app) diff --git a/frontend/src/lib/components/apps/editor/AppExportButton.svelte b/frontend/src/lib/components/apps/editor/AppExportButton.svelte index 19c881b172..7c255d4cfb 100644 --- a/frontend/src/lib/components/apps/editor/AppExportButton.svelte +++ b/frontend/src/lib/components/apps/editor/AppExportButton.svelte @@ -9,11 +9,17 @@ import { Button } from '../../common' import type { App } from '../types' import { Clipboard } from 'lucide-svelte' + import { yaml } from 'svelte-highlight/languages' + import YAML from 'yaml' + import Tabs from '$lib/components/common/tabs/Tabs.svelte' + import Tab from '$lib/components/common/tabs/Tab.svelte' let jsonViewerDrawer: Drawer let app: App | undefined = undefined + let rawType: 'json' | 'yaml' = 'yaml' + export function open(app_l: App) { app = app_l jsonViewerDrawer?.toggleDrawer() @@ -21,19 +27,38 @@ - jsonViewerDrawer.toggleDrawer()}> -
- - -
-
+ jsonViewerDrawer.toggleDrawer()}> +
+ + YAML + JSON + +
+ + +
+
+
+
diff --git a/frontend/src/lib/components/details/DetailPageDetailPanel.svelte b/frontend/src/lib/components/details/DetailPageDetailPanel.svelte index f34879bc69..3d9d80f4de 100644 --- a/frontend/src/lib/components/details/DetailPageDetailPanel.svelte +++ b/frontend/src/lib/components/details/DetailPageDetailPanel.svelte @@ -3,8 +3,10 @@ import { copyToClipboard } from '$lib/utils' import { CalendarCheck2, Clipboard, Terminal, Webhook } from 'lucide-svelte' import { Highlight } from 'svelte-highlight' + import { yaml } from 'svelte-highlight/languages' import json from 'svelte-highlight/languages/json' import { Pane, Splitpanes } from 'svelte-splitpanes' + import YAML from 'yaml' let triggerSelected: 'webhooks' | 'schedule' | 'cli' = 'webhooks' export let flow_json: any | undefined = undefined @@ -13,6 +15,7 @@ export let isOperator: boolean = false let selected = 'saved_inputs' + let rawType: 'json' | 'yaml' = 'yaml' $: if (hasStepDetails) { selected = 'flow_step' @@ -30,7 +33,7 @@ Details & Triggers {/if} {#if flow_json} - JSON + Raw {/if} {#if hasStepDetails} Step @@ -81,20 +84,37 @@ - -
- - -
+ + + YAML + JSON + +
+ + +
+
+
diff --git a/frontend/src/lib/components/flows/CreateActionsApp.svelte b/frontend/src/lib/components/flows/CreateActionsApp.svelte index 71811ecded..d15470f9ce 100644 --- a/frontend/src/lib/components/flows/CreateActionsApp.svelte +++ b/frontend/src/lib/components/flows/CreateActionsApp.svelte @@ -11,17 +11,20 @@ import { workspaceStore } from '$lib/stores' import Path from '../Path.svelte' import Tooltip from '../Tooltip.svelte' + import YAML from 'yaml' let drawer: Drawer | undefined = undefined let rawAppDrawer: Drawer | undefined = undefined - let pendingJson: string = '' + let pendingRaw: string = '' let pendingCode: string = '' let summary: string = '' let path: string = '' let pathError: string = '' - async function importJson() { - $importStore = JSON.parse(pendingJson) + let importType: 'yaml' | 'json' = 'yaml' + + async function importRaw() { + $importStore = importType === 'yaml' ? YAML.parse(pendingRaw) : JSON.parse(pendingRaw) await goto('/apps/add?nodraft=true') drawer?.closeDrawer?.() } @@ -48,9 +51,19 @@ startIcon={{ icon: Plus }} href="/apps/add?nodraft=true" dropdownItems={[ + { + label: 'Import low-code app from YAML', + onClick: () => { + drawer?.toggleDrawer?.() + importType = 'yaml' + } + }, { label: 'Import low-code app from JSON', - onClick: () => drawer?.toggleDrawer?.() + onClick: () => { + drawer?.toggleDrawer?.() + importType = 'json' + } }, { label: 'Import app in React/Vue/Svelte', @@ -66,10 +79,18 @@ - drawer?.toggleDrawer?.()}> - + drawer?.toggleDrawer?.()} + > + - + diff --git a/frontend/src/lib/components/flows/CreateActionsFlow.svelte b/frontend/src/lib/components/flows/CreateActionsFlow.svelte index b2afab9700..177504391b 100644 --- a/frontend/src/lib/components/flows/CreateActionsFlow.svelte +++ b/frontend/src/lib/components/flows/CreateActionsFlow.svelte @@ -8,12 +8,14 @@ import SimpleEditor from '$lib/components/SimpleEditor.svelte' import { importFlowStore } from '$lib/components/flows/flowStore' import { Plus } from 'lucide-svelte' + import YAML from 'yaml' let drawer: Drawer | undefined = undefined - let pendingJson: string + let pendingRaw: string + let importType: 'yaml' | 'json' = 'yaml' - async function importJson() { - $importFlowStore = JSON.parse(pendingJson) + async function importRaw() { + $importFlowStore = importType === 'yaml' ? YAML.parse(pendingRaw) : JSON.parse(pendingRaw) await goto('/flows/add') drawer?.closeDrawer?.() } @@ -28,9 +30,19 @@ endIcon={{ icon: BarsStaggered }} href="/flows/add?nodraft=true" dropdownItems={[ + { + label: 'Import from YAML', + onClick: () => { + drawer?.toggleDrawer?.() + importType = 'yaml' + } + }, { label: 'Import from JSON', - onClick: () => drawer?.toggleDrawer?.() + onClick: () => { + drawer?.toggleDrawer?.() + importType = 'json' + } } ]} > @@ -40,10 +52,18 @@ - drawer?.toggleDrawer?.()}> - + drawer?.toggleDrawer?.()} + > + - + diff --git a/frontend/src/lib/components/flows/header/FlowImportExportMenu.svelte b/frontend/src/lib/components/flows/header/FlowImportExportMenu.svelte index 305e449500..3a98700c54 100644 --- a/frontend/src/lib/components/flows/header/FlowImportExportMenu.svelte +++ b/frontend/src/lib/components/flows/header/FlowImportExportMenu.svelte @@ -21,13 +21,13 @@ on:click={() => jsonViewerDrawer.toggleDrawer()} startIcon={{ icon: FileJson }} > - JSON + Export - jsonViewerDrawer.toggleDrawer()}> + jsonViewerDrawer.toggleDrawer()}> {#if $flowStore} - + {/if} diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts index 1210ed661f..e5502be6db 100644 --- a/frontend/src/lib/utils.ts +++ b/frontend/src/lib/utils.ts @@ -8,6 +8,7 @@ // import { get } from 'svelte/store' import { deepEqual } from 'fast-equals' +import YAML from 'yaml' import type { UserExt } from './stores' import { sendUserToast } from './toast' import type { Script } from './gen' @@ -692,3 +693,9 @@ export function orderedJsonStringify(obj: any, space?: string | number) { JSON.stringify(obj, (key, value) => (allKeys.add(key), value)) return JSON.stringify(obj, (Array.from(allKeys) as string[]).sort(), space) } + +export function orderedYamlStringify(obj: any) { + const allKeys = new Set() + YAML.stringify(obj, (key, value) => (allKeys.add(key), value)) + return YAML.stringify(obj, (Array.from(allKeys) as string[]).sort()) +} diff --git a/frontend/src/routes/(root)/(logged)/apps/add/+page.svelte b/frontend/src/routes/(root)/(logged)/apps/add/+page.svelte index 851c1a19e9..1860ee375c 100644 --- a/frontend/src/routes/(root)/(logged)/apps/add/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/apps/add/+page.svelte @@ -16,7 +16,7 @@ const templatePath = $page.url.searchParams.get('template') const templateId = $page.url.searchParams.get('template_id') - const importJson = $importStore + const importRaw = $importStore if ($importStore) { $importStore = undefined } @@ -50,14 +50,14 @@ loadApp() async function loadApp() { - if (importJson) { - sendUserToast('Loaded from JSON') - if ('value' in importJson) { - summary = importJson.summary - value = importJson.value - policy = importJson.policy + if (importRaw) { + sendUserToast('Loaded from YAML/JSON') + if ('value' in importRaw) { + summary = importRaw.summary + value = importRaw.value + policy = importRaw.policy } else { - value = importJson + value = importRaw } } else if (templatePath) { const template = await AppService.getAppByPath({ diff --git a/frontend/src/routes/(root)/(logged)/flows/add/+page.svelte b/frontend/src/routes/(root)/(logged)/flows/add/+page.svelte index d4278fbbfe..bcf02c5f02 100644 --- a/frontend/src/routes/(root)/(logged)/flows/add/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/flows/add/+page.svelte @@ -56,7 +56,7 @@ if ($importFlowStore) { flow = $importFlowStore $importFlowStore = undefined - sendUserToast('Flow loaded from JSON') + sendUserToast('Flow loaded from YAML/JSON') } else if (!templatePath && !hubId && state) { sendUserToast('Flow restored from draft', false, [ {