From 348bbaf19b6dcbc935a9785005240ad0dcce2a8d Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 23 Sep 2025 18:35:45 +0200 Subject: [PATCH] Add YAML multiline support Signed-off-by: Andrei Kvapil --- .dockerignore | 1 + package-lock.json | 19 +++- package.json | 5 +- .../forms/formSync/utils/multilineHandler.ts | 105 ++++++++++++++++++ .../forms/formSync/utils/onValuesChange.ts | 4 +- .../forms/formSync/utils/onYamlChange.ts | 1 + 6 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 .dockerignore create mode 100644 src/endpoints/forms/formSync/utils/multilineHandler.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +node_modules diff --git a/package-lock.json b/package-lock.json index 28a6290..bb97d4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,8 @@ "swagger-autogen": "2.23.7", "swagger-ui-express": "5.0.1", "uuid": "10.0.0", - "winston": "3.17.0" + "winston": "3.17.0", + "yaml": "^2.3.4" }, "devDependencies": { "@types/cors": "2.8.17", @@ -37,7 +38,7 @@ "@types/lodash": "4.17.19", "@types/node": "20.14.2", "@types/uuid": "10.0.0", - "@types/ws": "^8.18.1", + "@types/ws": "8.18.1", "cross-env": "7.0.3", "eslint-config-prettier": "9.0.0", "eslint-plugin-import": "2.28.1", @@ -47,7 +48,7 @@ "ts-node": "10.9.2", "tsc-alias": "1.8.16", "tsconfig-paths": "4.2.0", - "typescript": "5.4.5", + "typescript": "^5.4.5", "typescript-eslint": "8.1.0" } }, @@ -5909,7 +5910,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6229,6 +6229,17 @@ } } }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 0ee5df4..a793d2f 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "swagger-autogen": "2.23.7", "swagger-ui-express": "5.0.1", "uuid": "10.0.0", - "winston": "3.17.0" + "winston": "3.17.0", + "yaml": "^2.3.4" }, "devDependencies": { "@types/cors": "2.8.17", @@ -53,7 +54,7 @@ "ts-node": "10.9.2", "tsc-alias": "1.8.16", "tsconfig-paths": "4.2.0", - "typescript": "5.4.5", + "typescript": "^5.4.5", "typescript-eslint": "8.1.0" } } diff --git a/src/endpoints/forms/formSync/utils/multilineHandler.ts b/src/endpoints/forms/formSync/utils/multilineHandler.ts new file mode 100644 index 0000000..804d2ec --- /dev/null +++ b/src/endpoints/forms/formSync/utils/multilineHandler.ts @@ -0,0 +1,105 @@ +import * as yaml from 'yaml' + +/** + * Processes multiline strings in YAML to ensure they are properly formatted + * @param yamlContent - The YAML content as string + * @returns Processed YAML content with proper multiline formatting + */ +export const processMultilineInYaml = (yamlContent: string): string => { + try { + // Parse the YAML to get the object structure + const parsed = yaml.parse(yamlContent) + + // Convert back to YAML with proper multiline formatting + const processed = yaml.stringify(parsed, { + // Use literal block scalar (|) for multiline strings + blockQuote: 'literal', + // Preserve line breaks + lineWidth: 0, + // Use double quotes for strings that need escaping + doubleQuotedAsJSON: false, + // Use literal block scalar for multiline strings + defaultStringType: 'QUOTE_DOUBLE', + }) + + return processed + } catch (error) { + // If parsing fails, return original content + console.warn('Failed to process multiline YAML:', error) + return yamlContent + } +} + +/** + * Processes form values to ensure multiline strings are properly handled + * @param values - The form values object + * @returns Processed values with multiline strings properly formatted + */ +export const processMultilineInFormValues = (values: any): any => { + if (!values || typeof values !== 'object') { + return values + } + + const processed = { ...values } + + const processObject = (obj: any): any => { + if (typeof obj === 'string') { + // Check if this string should be multiline + if (obj.includes('\n') || obj.length > 80) { + return obj + } + return obj + } + + if (Array.isArray(obj)) { + return obj.map(processObject) + } + + if (obj && typeof obj === 'object') { + const result: any = {} + for (const [key, value] of Object.entries(obj)) { + result[key] = processObject(value) + } + return result + } + + return obj + } + + return processObject(processed) +} + +/** + * Detects if a string value should be treated as multiline + * @param value - The string value to check + * @returns true if the value should be multiline + */ +export const shouldBeMultiline = (value: string): boolean => { + if (!value || typeof value !== 'string') { + return false + } + + // Check for newlines + if (value.includes('\n')) { + return true + } + + // Check for long strings + if (value.length > 80) { + return true + } + + // Check for multiline indicators + const multilineIndicators = [ + '#cloud-config', + '#!/', + '---', + '```', + 'BEGIN', + 'END', + '-----BEGIN', + '-----END', + ] + + return multilineIndicators.some(indicator => value.includes(indicator)) +} diff --git a/src/endpoints/forms/formSync/utils/onValuesChange.ts b/src/endpoints/forms/formSync/utils/onValuesChange.ts index a2c6a68..4ecfb50 100644 --- a/src/endpoints/forms/formSync/utils/onValuesChange.ts +++ b/src/endpoints/forms/formSync/utils/onValuesChange.ts @@ -2,6 +2,7 @@ import { OpenAPIV2 } from 'openapi-types' import { TFormName } from 'src/localTypes/forms' import { removeEmptyFormValues, renameBrokenFieldBack } from './removeAndRename' import { normalizeValuesForQuotas } from './normalizeQuotas' +import { processMultilineInFormValues } from './multilineHandler' export const onValuesChange = ({ values, @@ -15,5 +16,6 @@ export const onValuesChange = ({ const cleanSchema = removeEmptyFormValues(values, persistedKeys) const fixedCleanSchema = renameBrokenFieldBack(cleanSchema) const quotasFixedSchema = normalizeValuesForQuotas(fixedCleanSchema, properties) - return quotasFixedSchema + const multilineProcessedSchema = processMultilineInFormValues(quotasFixedSchema) + return multilineProcessedSchema } diff --git a/src/endpoints/forms/formSync/utils/onYamlChange.ts b/src/endpoints/forms/formSync/utils/onYamlChange.ts index a11967a..3ca6f9c 100644 --- a/src/endpoints/forms/formSync/utils/onYamlChange.ts +++ b/src/endpoints/forms/formSync/utils/onYamlChange.ts @@ -1,6 +1,7 @@ import { OpenAPIV2 } from 'openapi-types' import { renameBrokenFieldBackToFormAgain } from './removeAndRename' import { normalizeValuesForQuotasToNumber } from './normalizeQuotas' +import { processMultilineInYaml } from './multilineHandler' export const onYamlChange = ({ values,