helpers.legacy.ts•4.62 kB
import { BaseConfig, JSONata, JSONSchema, RequestOptions } from "@superglue/client";
import type { DataStore, Metadata } from "@superglue/shared";
import { generateWorkingTransform } from "../tools/tool-transform.js";
import { isSelfHealingEnabled, transformData, validateSchema } from "./helpers.js";
export function oldReplaceVariables(template: string, variables: Record<string, any>): string {
if (!template) return "";
const variableNames = Object.keys(variables);
const pattern = new RegExp(`\\{(${variableNames.join('|')})(?:\\.(\\w+))*\\}`, 'g');
return String(template).replace(pattern, (match, path) => {
const parts = path.split('.');
let value = variables;
for (const part of parts) {
if (value === undefined || value === null) {
return match; // Keep original if path is invalid
}
value = value[part];
}
if (value === undefined || value === null) {
if (path == 'cursor') {
return "";
}
return match; // Keep original if final value is invalid
}
if (Array.isArray(value) || typeof value === 'object') {
return JSON.stringify(value);
}
return String(value);
});
}
// Legacy function needs to stay for existing workflow backwards compatibility
export function flattenObject(obj: any, parentKey = '', res: Record<string, any> = {}): Record<string, any> {
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
const propName = parentKey ? `${parentKey}_${key}` : key;
if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
flattenObject(obj[key], propName, res);
} else {
res[propName] = obj[key];
}
}
}
return res;
}
export interface TransformConfig extends BaseConfig {
instruction: string;
responseSchema: JSONSchema;
responseMapping?: JSONata;
}
export type TransformInputRequest = {
id?: string;
endpoint?: TransformConfig;
};
export async function executeTransformLegacy(args: {
datastore: DataStore,
fromCache: boolean,
input: TransformInputRequest,
data: any,
options?: RequestOptions,
metadata: Metadata
}): Promise<{ data?: any; config?: TransformConfig }> {
const { datastore, fromCache, input, data, metadata, options } = args;
let currentConfig = input.endpoint;
if (fromCache && datastore) {
const cached = await datastore.getTransformConfig(input.id || input.endpoint.id, metadata.orgId);
if (cached) {
currentConfig = { ...cached, ...input.endpoint };
}
}
if (!currentConfig) {
throw new Error("No transform config found");
}
try {
if (!currentConfig?.responseMapping) {
throw new Error("No response mapping found");
}
const transformResult = await transformData(
data,
currentConfig.responseMapping
);
if (currentConfig.responseSchema) {
const validatedResult = await validateSchema(transformResult.data, currentConfig.responseSchema);
if (!validatedResult.success) {
throw new Error(`Schema validation failed: ${validatedResult.error}`);
}
}
if (!transformResult.success) {
throw new Error(transformResult.error);
}
return {
data: transformResult.data,
config: currentConfig
};
} catch (error) {
const rawErrorString = error?.message || JSON.stringify(error || {});
const transformError = rawErrorString.slice(0, 200);
let instruction = currentConfig.instruction;
if (transformError && currentConfig.responseMapping) {
instruction = `${instruction}\n\nThe previous error was: ${transformError} for the following mapping: ${currentConfig.responseMapping}`;
}
// if the transform is not self healing and there is an existing mapping, throw an error
// if there is no mapping that means that the config is being generated for the first time and should generate regardless
if (currentConfig.responseMapping && !isSelfHealingEnabled(options, "transform")) {
throw new Error(transformError);
}
const result = await generateWorkingTransform({
targetSchema: currentConfig.responseSchema,
inputData: data,
instruction: instruction,
metadata: metadata
});
if (!result || !result?.transformCode) {
throw new Error("Failed to generate transformation code.");
}
currentConfig = {
id: crypto.randomUUID(),
createdAt: new Date(),
updatedAt: new Date(),
...currentConfig,
responseMapping: result.transformCode
};
return {
data: data,
config: currentConfig
};
}
}