@octaviaflow/flow-rules 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/catalog/general/query/config.d.ts +43 -16
- package/dist/catalog/general/query/index.d.ts +4 -4
- package/dist/catalog/general/query/types.d.ts +157 -79
- package/dist/catalog/storage/csv-action/config.d.ts +2 -2
- package/dist/catalog/storage/excel-action/config.d.ts +2 -2
- package/dist/catalog/storage/fileEditContract.d.ts +2 -2
- package/dist/catalog/storage/fileHelp.d.ts +51 -0
- package/dist/catalog/storage/json-action/config.d.ts +2 -2
- package/dist/catalog/storage/xml-action/config.d.ts +2 -2
- package/dist/index.cjs +370 -237
- package/dist/index.d.ts +3 -2
- package/dist/index.js +370 -237
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -45,7 +45,6 @@ __export(exports_src, {
|
|
|
45
45
|
ActionStatus: () => ActionStatus,
|
|
46
46
|
ActionType: () => ActionType,
|
|
47
47
|
AggregateConfig: () => AggregateConfig,
|
|
48
|
-
AggregateFunction: () => AggregateFunction,
|
|
49
48
|
AiAgentConfig: () => AiAgentConfig,
|
|
50
49
|
CONDITION_OPERATORS: () => CONDITION_OPERATORS,
|
|
51
50
|
CONFIG_CONTRACTS: () => CONFIG_CONTRACTS,
|
|
@@ -67,6 +66,8 @@ __export(exports_src, {
|
|
|
67
66
|
FILE_EDIT_SCHEMA: () => FILE_EDIT_SCHEMA,
|
|
68
67
|
FILE_EDIT_SOURCES: () => FILE_EDIT_SOURCES,
|
|
69
68
|
FILE_FORMAT_OPERATIONS: () => FILE_FORMAT_OPERATIONS,
|
|
69
|
+
FILE_INPUT_HELP: () => FILE_INPUT_HELP,
|
|
70
|
+
FILE_OUTPUT_HELP: () => FILE_OUTPUT_HELP,
|
|
70
71
|
FTP_OPERATIONS: () => FTP_OPERATIONS,
|
|
71
72
|
FieldType: () => FieldType,
|
|
72
73
|
ForEachConfig: () => ForEachConfig,
|
|
@@ -76,22 +77,33 @@ __export(exports_src, {
|
|
|
76
77
|
GroupConfig: () => GroupConfig,
|
|
77
78
|
IDP_EXTRACT_STRATEGIES: () => IDP_EXTRACT_STRATEGIES,
|
|
78
79
|
IdpExtractConfig: () => IdpExtractConfig,
|
|
79
|
-
JoinType: () => JoinType,
|
|
80
80
|
JsonActionConfig: () => JsonActionConfig,
|
|
81
|
-
LogicalOperator: () => LogicalOperator,
|
|
82
81
|
MAPPING_FUNCTIONS: () => MAPPING_FUNCTIONS,
|
|
83
82
|
MAPPING_FUNCTION_SPECS: () => MAPPING_FUNCTION_SPECS,
|
|
83
|
+
MAX_CONDITION_DEPTH: () => MAX_CONDITION_DEPTH,
|
|
84
84
|
MAX_STEP_CONFIG_BYTES: () => MAX_STEP_CONFIG_BYTES,
|
|
85
85
|
ManualTriggerConfig: () => ManualTriggerConfig,
|
|
86
86
|
NodeStatus: () => NodeStatus,
|
|
87
87
|
ONEDRIVE_OPERATIONS: () => ONEDRIVE_OPERATIONS,
|
|
88
88
|
OneDriveActionConfig: () => OneDriveActionConfig,
|
|
89
89
|
PLATFORM_CONFIG_KEYS: () => PLATFORM_CONFIG_KEYS,
|
|
90
|
+
QUERY_AGGREGATE_FUNCTIONS: () => QUERY_AGGREGATE_FUNCTIONS,
|
|
91
|
+
QUERY_AUTHORED_SQL_MESSAGE: () => QUERY_AUTHORED_SQL_MESSAGE,
|
|
92
|
+
QUERY_COMPARISON_OPERATORS: () => QUERY_COMPARISON_OPERATORS,
|
|
93
|
+
QUERY_CONDITION_OPERATORS: () => QUERY_CONDITION_OPERATORS,
|
|
94
|
+
QUERY_MODES: () => QUERY_MODES,
|
|
95
|
+
QUERY_RELATIVE_DATE_OPERATORS: () => QUERY_RELATIVE_DATE_OPERATORS,
|
|
96
|
+
QUERY_TYPES: () => QUERY_TYPES,
|
|
97
|
+
QUERY_VISUAL_ONLY_MESSAGE: () => QUERY_VISUAL_ONLY_MESSAGE,
|
|
90
98
|
QueryMode: () => QueryMode,
|
|
91
99
|
QueryType: () => QueryType,
|
|
100
|
+
RELATIVE_DATE_DIRECTIONS: () => RELATIVE_DATE_DIRECTIONS,
|
|
101
|
+
RELATIVE_DATE_MAX_AMOUNT: () => RELATIVE_DATE_MAX_AMOUNT,
|
|
102
|
+
RELATIVE_DATE_UNITS: () => RELATIVE_DATE_UNITS,
|
|
92
103
|
S3ActionConfig: () => S3ActionConfig,
|
|
93
104
|
S3_OPERATIONS: () => S3_OPERATIONS,
|
|
94
105
|
STORAGE_CONNECTOR_IDS: () => STORAGE_CONNECTOR_IDS,
|
|
106
|
+
STORAGE_DOWNLOAD_OUTPUT_SCHEMA: () => STORAGE_DOWNLOAD_OUTPUT_SCHEMA,
|
|
95
107
|
SortDirection: () => SortDirection,
|
|
96
108
|
StorageActionConfig: () => StorageActionConfig,
|
|
97
109
|
TRANSFORM_MODES: () => TRANSFORM_MODES,
|
|
@@ -131,6 +143,7 @@ __export(exports_src, {
|
|
|
131
143
|
getStorageProvider: () => getStorageProvider,
|
|
132
144
|
groupKeyName: () => groupKeyName,
|
|
133
145
|
inferSchemaFromSample: () => inferSchemaFromSample,
|
|
146
|
+
isConditionGroup: () => isConditionGroup,
|
|
134
147
|
isEditorPrivateKey: () => isEditorPrivateKey,
|
|
135
148
|
isEventCapableConnector: () => isEventCapableConnector,
|
|
136
149
|
isMappingFunction: () => isMappingFunction,
|
|
@@ -138,6 +151,8 @@ __export(exports_src, {
|
|
|
138
151
|
mappingTransformFunctionName: () => mappingTransformFunctionName,
|
|
139
152
|
migrateDelayConfig: () => migrateDelayConfig,
|
|
140
153
|
queryDefinition: () => queryDefinition,
|
|
154
|
+
queryFieldsSatisfied: () => queryFieldsSatisfied,
|
|
155
|
+
queryTablesSatisfied: () => queryTablesSatisfied,
|
|
141
156
|
resolveRecordSchema: () => resolveRecordSchema,
|
|
142
157
|
selectsWithoutValues: () => selectsWithoutValues,
|
|
143
158
|
storageConfigKeys: () => storageConfigKeys,
|
|
@@ -146,12 +161,14 @@ __export(exports_src, {
|
|
|
146
161
|
transformConnectionToEdge: () => transformConnectionToEdge,
|
|
147
162
|
transformFlowToWorkflow: () => transformFlowToWorkflow,
|
|
148
163
|
transformStepToNode: () => transformStepToNode,
|
|
164
|
+
validateBuildQueryConfig: () => validateBuildQueryConfig,
|
|
149
165
|
validateConnectorTriggerEvents: () => validateConnectorTriggerEvents,
|
|
150
166
|
validateEdge: () => validateEdge,
|
|
151
167
|
validateFlowData: () => validateFlowData,
|
|
152
168
|
validateNode: () => validateNode,
|
|
153
169
|
validateStepConfig: () => validateStepConfig,
|
|
154
170
|
validateStepConfigs: () => validateStepConfigs,
|
|
171
|
+
validateVisualConfig: () => validateVisualConfig,
|
|
155
172
|
validateWorkflow: () => validateWorkflow
|
|
156
173
|
});
|
|
157
174
|
module.exports = __toCommonJS(exports_src);
|
|
@@ -1180,32 +1197,51 @@ var QueryType;
|
|
|
1180
1197
|
QueryType["Delete"] = "delete";
|
|
1181
1198
|
QueryType["Custom"] = "custom";
|
|
1182
1199
|
})(QueryType ||= {});
|
|
1183
|
-
var
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1200
|
+
var QUERY_MODES = ["visual" /* Visual */];
|
|
1201
|
+
var QUERY_TYPES = ["select" /* Select */];
|
|
1202
|
+
var QUERY_COMPARISON_OPERATORS = [
|
|
1203
|
+
"=",
|
|
1204
|
+
"!=",
|
|
1205
|
+
">",
|
|
1206
|
+
"<",
|
|
1207
|
+
">=",
|
|
1208
|
+
"<=",
|
|
1209
|
+
"equals",
|
|
1210
|
+
"notEquals",
|
|
1211
|
+
"greaterThan",
|
|
1212
|
+
"lessThan",
|
|
1213
|
+
"greaterOrEqual",
|
|
1214
|
+
"lessOrEqual"
|
|
1215
|
+
];
|
|
1216
|
+
var QUERY_RELATIVE_DATE_OPERATORS = ["inRelative", "notInRelative"];
|
|
1217
|
+
var QUERY_CONDITION_OPERATORS = [
|
|
1218
|
+
...QUERY_COMPARISON_OPERATORS,
|
|
1219
|
+
"contains",
|
|
1220
|
+
"startsWith",
|
|
1221
|
+
"endsWith",
|
|
1222
|
+
"like",
|
|
1223
|
+
"notLike",
|
|
1224
|
+
"in",
|
|
1225
|
+
"notIn",
|
|
1226
|
+
"between",
|
|
1227
|
+
"isNull",
|
|
1228
|
+
"isNotNull",
|
|
1229
|
+
...QUERY_RELATIVE_DATE_OPERATORS
|
|
1230
|
+
];
|
|
1231
|
+
var RELATIVE_DATE_DIRECTIONS = ["last", "next", "this"];
|
|
1232
|
+
var RELATIVE_DATE_UNITS = ["day", "week", "month", "quarter", "year"];
|
|
1233
|
+
var RELATIVE_DATE_MAX_AMOUNT = 3650;
|
|
1234
|
+
var MAX_CONDITION_DEPTH = 3;
|
|
1235
|
+
var isConditionGroup = (c) => !!c && typeof c === "object" && c.type === "group";
|
|
1236
|
+
var QUERY_AGGREGATE_FUNCTIONS = ["COUNT", "COUNT_DISTINCT", "SUM", "AVG", "MIN", "MAX"];
|
|
1188
1237
|
var SortDirection;
|
|
1189
1238
|
((SortDirection) => {
|
|
1190
1239
|
SortDirection["Ascending"] = "asc";
|
|
1191
1240
|
SortDirection["Descending"] = "desc";
|
|
1192
1241
|
})(SortDirection ||= {});
|
|
1193
|
-
|
|
1194
|
-
(
|
|
1195
|
-
|
|
1196
|
-
JoinType["Left"] = "left";
|
|
1197
|
-
JoinType["Right"] = "right";
|
|
1198
|
-
JoinType["Full"] = "full";
|
|
1199
|
-
})(JoinType ||= {});
|
|
1200
|
-
var AggregateFunction;
|
|
1201
|
-
((AggregateFunction) => {
|
|
1202
|
-
AggregateFunction["Count"] = "count";
|
|
1203
|
-
AggregateFunction["Sum"] = "sum";
|
|
1204
|
-
AggregateFunction["Average"] = "avg";
|
|
1205
|
-
AggregateFunction["Min"] = "min";
|
|
1206
|
-
AggregateFunction["Max"] = "max";
|
|
1207
|
-
AggregateFunction["CountDistinct"] = "count_distinct";
|
|
1208
|
-
})(AggregateFunction ||= {});
|
|
1242
|
+
// src/catalog/isOneOf.ts
|
|
1243
|
+
var isOneOf = (set, value) => typeof value === "string" && set.includes(value);
|
|
1244
|
+
|
|
1209
1245
|
// src/catalog/general/query/config.ts
|
|
1210
1246
|
var BuildQueryConfigDefaults = {
|
|
1211
1247
|
mode: "visual" /* Visual */,
|
|
@@ -1215,160 +1251,262 @@ var BuildQueryConfigDefaults = {
|
|
|
1215
1251
|
visualConfig: {
|
|
1216
1252
|
tables: [],
|
|
1217
1253
|
fields: [],
|
|
1218
|
-
|
|
1254
|
+
conditions: [],
|
|
1219
1255
|
joins: [],
|
|
1220
1256
|
groupBy: [],
|
|
1257
|
+
aggregates: [],
|
|
1221
1258
|
sort: [],
|
|
1222
|
-
limit: 10
|
|
1223
|
-
offset: 0
|
|
1224
|
-
},
|
|
1225
|
-
sqlConfig: {
|
|
1226
|
-
sql: "",
|
|
1227
|
-
parameters: {}
|
|
1259
|
+
limit: 10
|
|
1228
1260
|
}
|
|
1229
1261
|
};
|
|
1230
|
-
var
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1262
|
+
var QUERY_VISUAL_ONLY_MESSAGE = "The Query step is built with the visual builder only — set mode to 'visual' and describe the query in " + "visualConfig (tables, fields, conditions, sort, groupBy, aggregates). Hand-written SQL is not accepted.";
|
|
1263
|
+
var QUERY_AUTHORED_SQL_MESSAGE = "sqlConfig.sql is written by the visual builder, not by hand — remove sqlConfig and describe the query in " + "visualConfig (tables, fields, conditions, sort, groupBy, aggregates) instead.";
|
|
1264
|
+
var err = (field, message) => ({ field, message, severity: "error" });
|
|
1265
|
+
var isRecord = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
1266
|
+
var nonEmptyString = (v) => typeof v === "string" && v.trim().length > 0;
|
|
1267
|
+
var hasValue = (v) => v !== undefined && v !== null && !(typeof v === "string" && v.trim() === "") && !(Array.isArray(v) && v.length === 0);
|
|
1268
|
+
var arr = (v) => Array.isArray(v) ? v : [];
|
|
1269
|
+
function queryFieldsSatisfied(config) {
|
|
1270
|
+
const vc = isRecord(config?.visualConfig) ? config.visualConfig : undefined;
|
|
1271
|
+
if (!vc)
|
|
1272
|
+
return false;
|
|
1273
|
+
if (vc.selectAll === true)
|
|
1274
|
+
return true;
|
|
1275
|
+
if (arr(vc.fields).length > 0)
|
|
1276
|
+
return true;
|
|
1277
|
+
if (arr(vc.aggregates).length > 0 || arr(vc.groupBy).length > 0)
|
|
1278
|
+
return true;
|
|
1279
|
+
const sql = isRecord(config?.sqlConfig) ? config.sqlConfig.sql : undefined;
|
|
1280
|
+
return arr(vc.tables).length > 0 && nonEmptyString(sql);
|
|
1281
|
+
}
|
|
1282
|
+
function queryTablesSatisfied(config) {
|
|
1283
|
+
const vc = isRecord(config?.visualConfig) ? config.visualConfig : undefined;
|
|
1284
|
+
if (!vc)
|
|
1285
|
+
return false;
|
|
1286
|
+
return vc.selectAll === true || arr(vc.tables).length > 0;
|
|
1287
|
+
}
|
|
1288
|
+
var OPERATOR_LIST = QUERY_CONDITION_OPERATORS.join(", ");
|
|
1289
|
+
function validateRelative(path, relative) {
|
|
1290
|
+
if (!isRecord(relative)) {
|
|
1291
|
+
return [err(`${path}.relative`, `${path}: a relative-date condition needs relative, e.g. { direction: 'last', amount: 30, unit: 'day' }`)];
|
|
1239
1292
|
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
severity: "error"
|
|
1245
|
-
});
|
|
1293
|
+
const issues = [];
|
|
1294
|
+
const { direction, unit, amount, includeCurrent } = relative;
|
|
1295
|
+
if (!isOneOf(RELATIVE_DATE_DIRECTIONS, direction)) {
|
|
1296
|
+
issues.push(err(`${path}.relative.direction`, `${path}.relative.direction must be one of: ${RELATIVE_DATE_DIRECTIONS.join(", ")} (got "${String(direction)}")`));
|
|
1246
1297
|
}
|
|
1247
|
-
if (!
|
|
1248
|
-
|
|
1249
|
-
field: "queryType",
|
|
1250
|
-
message: "Query type is required",
|
|
1251
|
-
severity: "error"
|
|
1252
|
-
});
|
|
1298
|
+
if (!isOneOf(RELATIVE_DATE_UNITS, unit)) {
|
|
1299
|
+
issues.push(err(`${path}.relative.unit`, `${path}.relative.unit must be one of: ${RELATIVE_DATE_UNITS.join(", ")} (got "${String(unit)}")`));
|
|
1253
1300
|
}
|
|
1254
|
-
if (
|
|
1255
|
-
const
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
errors.push(...sqlErrors);
|
|
1301
|
+
if (direction === "last" || direction === "next") {
|
|
1302
|
+
const ok = typeof amount === "number" && Number.isInteger(amount) && amount >= 1 && amount <= RELATIVE_DATE_MAX_AMOUNT;
|
|
1303
|
+
if (!ok) {
|
|
1304
|
+
issues.push(err(`${path}.relative.amount`, `${path}.relative.amount must be a whole number from 1 to ${RELATIVE_DATE_MAX_AMOUNT} for '${direction}' ` + `(e.g. last 30 days is { direction: 'last', amount: 30, unit: 'day' })`));
|
|
1305
|
+
}
|
|
1260
1306
|
}
|
|
1261
|
-
if (
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1307
|
+
if (includeCurrent !== undefined && typeof includeCurrent !== "boolean") {
|
|
1308
|
+
issues.push(err(`${path}.relative.includeCurrent`, `${path}.relative.includeCurrent must be true or false`));
|
|
1309
|
+
}
|
|
1310
|
+
return issues;
|
|
1311
|
+
}
|
|
1312
|
+
function validateRow(path, row, inHaving) {
|
|
1313
|
+
const issues = [];
|
|
1314
|
+
if (!nonEmptyString(row.field)) {
|
|
1315
|
+
issues.push(err(`${path}.field`, `${path}: choose the field to compare (field is empty)`));
|
|
1316
|
+
}
|
|
1317
|
+
if (!isOneOf(QUERY_CONDITION_OPERATORS, row.operator)) {
|
|
1318
|
+
issues.push(err(`${path}.operator`, `${path}.operator must be one of: ${OPERATOR_LIST} (got "${String(row.operator)}")`));
|
|
1319
|
+
return issues;
|
|
1320
|
+
}
|
|
1321
|
+
const op = row.operator;
|
|
1322
|
+
const isRelative = isOneOf(QUERY_RELATIVE_DATE_OPERATORS, op);
|
|
1323
|
+
const comparesField = row.valueField !== undefined && row.valueField !== null && row.valueField !== "";
|
|
1324
|
+
if (isRelative) {
|
|
1325
|
+
if (inHaving) {
|
|
1326
|
+
issues.push(err(`${path}.operator`, `${path}: '${op}' filters dates on rows — put it in conditions, not having`));
|
|
1327
|
+
} else {
|
|
1328
|
+
issues.push(...validateRelative(path, row.relative));
|
|
1329
|
+
}
|
|
1330
|
+
if (hasValue(row.value) || comparesField) {
|
|
1331
|
+
issues.push(err(path, `${path}: a relative-date condition takes its window from relative — remove value and valueField`));
|
|
1274
1332
|
}
|
|
1333
|
+
} else if (row.relative !== undefined) {
|
|
1334
|
+
issues.push(err(`${path}.relative`, `${path}: relative is only used with 'inRelative' or 'notInRelative' — change the operator or remove relative`));
|
|
1275
1335
|
}
|
|
1276
|
-
if (
|
|
1277
|
-
if (
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
});
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
message: "Max rows is very high (>10,000). This may impact performance.",
|
|
1287
|
-
severity: "warning"
|
|
1288
|
-
});
|
|
1336
|
+
if (comparesField) {
|
|
1337
|
+
if (inHaving) {
|
|
1338
|
+
issues.push(err(`${path}.valueField`, `${path}: having compares with a value — use value, not valueField`));
|
|
1339
|
+
} else if (!nonEmptyString(row.valueField)) {
|
|
1340
|
+
issues.push(err(`${path}.valueField`, `${path}.valueField must name the field to compare with`));
|
|
1341
|
+
} else if (!isOneOf(QUERY_COMPARISON_OPERATORS, op)) {
|
|
1342
|
+
issues.push(err(`${path}.valueField`, `${path}: comparing with another field works with =, !=, >, <, >=, <= only (got "${op}")`));
|
|
1343
|
+
}
|
|
1344
|
+
if (hasValue(row.value)) {
|
|
1345
|
+
issues.push(err(path, `${path}: set either value (compare with a value) or valueField (compare with another field), not both`));
|
|
1289
1346
|
}
|
|
1290
1347
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1348
|
+
if (row.joinOperator !== undefined && !(typeof row.joinOperator === "string" && /^(and|or)$/i.test(row.joinOperator))) {
|
|
1349
|
+
issues.push(err(`${path}.joinOperator`, `${path}.joinOperator must be AND or OR (got "${String(row.joinOperator)}")`));
|
|
1350
|
+
}
|
|
1351
|
+
return issues;
|
|
1352
|
+
}
|
|
1353
|
+
function validateConditionList(path, list, depth) {
|
|
1354
|
+
if (list === undefined || list === null)
|
|
1355
|
+
return [];
|
|
1356
|
+
if (!Array.isArray(list))
|
|
1357
|
+
return [err(path, `${path} must be a list of conditions`)];
|
|
1358
|
+
const issues = [];
|
|
1359
|
+
list.forEach((item, i) => {
|
|
1360
|
+
const at = `${path}[${i}]`;
|
|
1361
|
+
if (!isRecord(item)) {
|
|
1362
|
+
issues.push(err(at, `${at} must be a condition ({ field, operator, value }) or a group ({ type: 'group', conditions })`));
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
if (isConditionGroup(item)) {
|
|
1366
|
+
if (depth + 1 > MAX_CONDITION_DEPTH) {
|
|
1367
|
+
issues.push(err(at, `${at}: groups can nest at most ${MAX_CONDITION_DEPTH} levels deep — move this group up a level or combine its conditions`));
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
if (item.joinOperator !== undefined && !(typeof item.joinOperator === "string" && /^(and|or)$/i.test(item.joinOperator))) {
|
|
1371
|
+
issues.push(err(`${at}.joinOperator`, `${at}.joinOperator must be AND or OR (got "${String(item.joinOperator)}")`));
|
|
1372
|
+
}
|
|
1373
|
+
if (!Array.isArray(item.conditions) || item.conditions.length === 0) {
|
|
1374
|
+
issues.push(err(`${at}.conditions`, `${at}: a group needs at least one condition — add one or remove the group`));
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
issues.push(...validateConditionList(`${at}.conditions`, item.conditions, depth + 1));
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
issues.push(...validateRow(at, item, false));
|
|
1381
|
+
});
|
|
1382
|
+
return issues;
|
|
1383
|
+
}
|
|
1384
|
+
function validateAggregates(vc) {
|
|
1385
|
+
const list = vc.aggregates;
|
|
1386
|
+
if (list === undefined || list === null)
|
|
1387
|
+
return [];
|
|
1388
|
+
if (!Array.isArray(list))
|
|
1389
|
+
return [err("visualConfig.aggregates", "visualConfig.aggregates must be a list of { fn, field, alias }")];
|
|
1390
|
+
const issues = [];
|
|
1391
|
+
list.forEach((a, i) => {
|
|
1392
|
+
const at = `visualConfig.aggregates[${i}]`;
|
|
1393
|
+
if (!isRecord(a)) {
|
|
1394
|
+
issues.push(err(at, `${at} must be { fn, field, alias }`));
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
const fn = typeof a.fn === "string" ? a.fn.toUpperCase() : a.fn;
|
|
1398
|
+
if (!isOneOf(QUERY_AGGREGATE_FUNCTIONS, fn)) {
|
|
1399
|
+
issues.push(err(`${at}.fn`, `${at}.fn must be one of: ${QUERY_AGGREGATE_FUNCTIONS.join(", ")} (got "${String(a.fn)}")`));
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
const allRows = !nonEmptyString(a.field) || a.field.trim() === "*";
|
|
1403
|
+
if (allRows && fn !== "COUNT") {
|
|
1404
|
+
issues.push(err(`${at}.field`, `${at}: ${fn} needs a field to work on — set field to a column name`));
|
|
1405
|
+
}
|
|
1406
|
+
if (a.alias !== undefined && a.alias !== null && !nonEmptyString(a.alias)) {
|
|
1407
|
+
issues.push(err(`${at}.alias`, `${at}.alias must be a name for the result column, or left out`));
|
|
1408
|
+
}
|
|
1409
|
+
});
|
|
1410
|
+
return issues;
|
|
1411
|
+
}
|
|
1412
|
+
function validateHaving(vc) {
|
|
1413
|
+
const list = vc.having;
|
|
1414
|
+
if (list === undefined || list === null || Array.isArray(list) && list.length === 0)
|
|
1415
|
+
return [];
|
|
1416
|
+
if (!Array.isArray(list))
|
|
1417
|
+
return [err("visualConfig.having", "visualConfig.having must be a list of conditions")];
|
|
1418
|
+
const aggregates = arr(vc.aggregates).filter(isRecord);
|
|
1419
|
+
const groupBy = arr(vc.groupBy).filter(nonEmptyString);
|
|
1420
|
+
if (aggregates.length === 0 && groupBy.length === 0) {
|
|
1421
|
+
return [err("visualConfig.having", "visualConfig.having filters grouped results, so it needs aggregates or groupBy — add them, or move these filters to conditions")];
|
|
1422
|
+
}
|
|
1423
|
+
const names = new Set([
|
|
1424
|
+
...groupBy,
|
|
1425
|
+
...aggregates.map((a) => a.alias).filter(nonEmptyString)
|
|
1426
|
+
]);
|
|
1427
|
+
const issues = [];
|
|
1428
|
+
list.forEach((row, i) => {
|
|
1429
|
+
const at = `visualConfig.having[${i}]`;
|
|
1430
|
+
if (!isRecord(row) || isConditionGroup(row)) {
|
|
1431
|
+
issues.push(err(at, `${at} must be a single condition ({ field, operator, value }); groups are not used in having`));
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
issues.push(...validateRow(at, row, true));
|
|
1435
|
+
if (nonEmptyString(row.field) && !names.has(row.field)) {
|
|
1436
|
+
const known = [...names];
|
|
1437
|
+
issues.push(err(`${at}.field`, `${at}.field must be an aggregate alias or a groupBy column (got "${row.field}"` + (known.length > 0 ? `; available: ${known.join(", ")})` : "; give the aggregate an alias first)")));
|
|
1438
|
+
}
|
|
1439
|
+
});
|
|
1440
|
+
return issues;
|
|
1441
|
+
}
|
|
1442
|
+
function validateVisualConfig(config) {
|
|
1443
|
+
const vc = config.visualConfig;
|
|
1444
|
+
if (!isRecord(vc)) {
|
|
1445
|
+
return [err("visualConfig", "visualConfig is required — describe the query with tables (what to read), fields (which columns) and optional conditions")];
|
|
1446
|
+
}
|
|
1447
|
+
const issues = [];
|
|
1448
|
+
if (!queryTablesSatisfied(config)) {
|
|
1449
|
+
issues.push(err("visualConfig.tables", "At least one table must be selected (visualConfig.tables)"));
|
|
1450
|
+
}
|
|
1451
|
+
if (!queryFieldsSatisfied(config)) {
|
|
1452
|
+
issues.push(err("visualConfig.fields", "At least one field must be selected (visualConfig.fields), or add aggregates / groupBy for a summary query"));
|
|
1453
|
+
}
|
|
1454
|
+
if (arr(vc.tables).length > 1 && arr(vc.joins).length === 0) {
|
|
1455
|
+
issues.push(err("visualConfig.joins", "Joins are required when selecting from multiple tables (visualConfig.joins)"));
|
|
1456
|
+
}
|
|
1457
|
+
if (vc.distinct !== undefined && typeof vc.distinct !== "boolean") {
|
|
1458
|
+
issues.push(err("visualConfig.distinct", "visualConfig.distinct must be true or false"));
|
|
1459
|
+
}
|
|
1460
|
+
issues.push(...validateConditionList("visualConfig.conditions", vc.conditions, 0));
|
|
1461
|
+
issues.push(...validateAggregates(vc));
|
|
1462
|
+
issues.push(...validateHaving(vc));
|
|
1463
|
+
return issues;
|
|
1464
|
+
}
|
|
1465
|
+
var validateBuildQueryConfig = (config) => {
|
|
1466
|
+
const c = config;
|
|
1298
1467
|
const errors = [];
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
message: "Visual query configuration is required",
|
|
1303
|
-
severity: "error"
|
|
1304
|
-
});
|
|
1305
|
-
return errors;
|
|
1468
|
+
const warnings = [];
|
|
1469
|
+
if (!c.connectorId) {
|
|
1470
|
+
errors.push(err("connectorId", "Please select a connector (connectorId)"));
|
|
1306
1471
|
}
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1472
|
+
const mode = c.mode;
|
|
1473
|
+
const visual = mode === undefined || mode === null || mode === "" || isOneOf(QUERY_MODES, mode);
|
|
1474
|
+
if (!visual) {
|
|
1475
|
+
errors.push(err("mode", `Mode must be one of: ${QUERY_MODES.join(", ")} (got "${String(mode)}"). ${QUERY_VISUAL_ONLY_MESSAGE}`));
|
|
1476
|
+
} else if (!mode) {
|
|
1477
|
+
errors.push(err("mode", "Query mode is required — set mode to 'visual'"));
|
|
1313
1478
|
}
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
});
|
|
1479
|
+
const queryType = c.queryType;
|
|
1480
|
+
if (!queryType) {
|
|
1481
|
+
errors.push(err("queryType", "Query type is required — set queryType to 'select'"));
|
|
1482
|
+
} else if (!isOneOf(QUERY_TYPES, queryType)) {
|
|
1483
|
+
errors.push(err("queryType", `Query type must be one of: ${QUERY_TYPES.join(", ")} (got "${String(queryType)}"). ` + "The Query step only reads data — use a Transform step to insert, update or delete."));
|
|
1320
1484
|
}
|
|
1321
|
-
if (
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
});
|
|
1485
|
+
if (visual) {
|
|
1486
|
+
const sql = isRecord(c.sqlConfig) ? c.sqlConfig.sql : undefined;
|
|
1487
|
+
if (nonEmptyString(sql) && !isRecord(c.visualConfig)) {
|
|
1488
|
+
errors.push(err("sqlConfig.sql", QUERY_AUTHORED_SQL_MESSAGE));
|
|
1489
|
+
} else {
|
|
1490
|
+
errors.push(...validateVisualConfig(c));
|
|
1328
1491
|
}
|
|
1329
1492
|
}
|
|
1330
|
-
const
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
message: "Group by is required when mixing aggregate and non-aggregate fields",
|
|
1337
|
-
severity: "error"
|
|
1338
|
-
});
|
|
1493
|
+
const timeout = c.timeout;
|
|
1494
|
+
if (typeof timeout === "number") {
|
|
1495
|
+
if (timeout < 1000) {
|
|
1496
|
+
errors.push(err("timeout", "Timeout must be at least 1000ms"));
|
|
1497
|
+
} else if (timeout > 300000) {
|
|
1498
|
+
warnings.push({ field: "timeout", message: "Timeout is very high (>5 minutes)", severity: "warning" });
|
|
1339
1499
|
}
|
|
1340
1500
|
}
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
field: "sqlConfig",
|
|
1348
|
-
message: "SQL query configuration is required",
|
|
1349
|
-
severity: "error"
|
|
1350
|
-
});
|
|
1351
|
-
return errors;
|
|
1352
|
-
}
|
|
1353
|
-
if (!config.sql || config.sql.trim().length === 0) {
|
|
1354
|
-
errors.push({
|
|
1355
|
-
field: "sqlConfig.sql",
|
|
1356
|
-
message: "SQL query is required",
|
|
1357
|
-
severity: "error"
|
|
1358
|
-
});
|
|
1359
|
-
} else {
|
|
1360
|
-
const sql = config.sql.trim().toLowerCase();
|
|
1361
|
-
const dangerousKeywords = ["drop", "truncate", "alter", "create", "grant", "revoke"];
|
|
1362
|
-
const hasDangerousKeyword = dangerousKeywords.some((keyword) => sql.includes(keyword + " "));
|
|
1363
|
-
if (hasDangerousKeyword) {
|
|
1364
|
-
errors.push({
|
|
1365
|
-
field: "sqlConfig.sql",
|
|
1366
|
-
message: "SQL query contains potentially dangerous operations (DROP, TRUNCATE, ALTER, etc.)",
|
|
1367
|
-
severity: "error"
|
|
1368
|
-
});
|
|
1501
|
+
const maxRows = c.maxRows;
|
|
1502
|
+
if (typeof maxRows === "number") {
|
|
1503
|
+
if (maxRows < 1) {
|
|
1504
|
+
errors.push(err("maxRows", "Max rows must be at least 1"));
|
|
1505
|
+
} else if (maxRows > 1e4) {
|
|
1506
|
+
warnings.push({ field: "maxRows", message: "Max rows is very high (>10,000). This may impact performance.", severity: "warning" });
|
|
1369
1507
|
}
|
|
1370
1508
|
}
|
|
1371
|
-
return errors;
|
|
1509
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
1372
1510
|
};
|
|
1373
1511
|
function isQueryableConnector(connector) {
|
|
1374
1512
|
if (connector.isQueryable !== undefined) {
|
|
@@ -1400,17 +1538,26 @@ var BuildQueryContractSchema = {
|
|
|
1400
1538
|
mode: {
|
|
1401
1539
|
type: "select",
|
|
1402
1540
|
required: true,
|
|
1403
|
-
options:
|
|
1404
|
-
description: "visual: the
|
|
1541
|
+
options: QUERY_MODES,
|
|
1542
|
+
description: "Always 'visual': the Query step is built with the visual builder and described in visualConfig. Hand-written SQL is not accepted."
|
|
1405
1543
|
},
|
|
1406
1544
|
queryType: {
|
|
1407
1545
|
type: "select",
|
|
1408
1546
|
required: true,
|
|
1409
|
-
options:
|
|
1410
|
-
description: "
|
|
1547
|
+
options: QUERY_TYPES,
|
|
1548
|
+
description: "Always 'select': the Query step reads data. Use a Transform step to write."
|
|
1549
|
+
},
|
|
1550
|
+
visualConfig: {
|
|
1551
|
+
type: "object",
|
|
1552
|
+
required: false,
|
|
1553
|
+
description: 'The query. tables: the table or object to read; fields: "table.column" names to return; ' + "conditions: filters { field, operator, value } (or valueField to compare two fields, or operator " + "'inRelative' with relative { direction: last|next|this, amount, unit: day|week|month|quarter|year } for date windows), " + `which may be grouped { type: 'group', conditions } up to ${MAX_CONDITION_DEPTH} levels deep and joined with AND/OR; ` + "distinct; groupBy; aggregates [{ fn: COUNT|COUNT_DISTINCT|SUM|AVG|MIN|MAX, field, alias }]; " + "having: filters on aggregate aliases or groupBy columns; sort [{ field, direction }]; maxRows: the run cap (up to 10,000)."
|
|
1554
|
+
},
|
|
1555
|
+
sqlConfig: {
|
|
1556
|
+
type: "object",
|
|
1557
|
+
required: false,
|
|
1558
|
+
derived: true,
|
|
1559
|
+
description: "Compiled from visualConfig by the editor or the server. Never write it — describe the query in visualConfig."
|
|
1411
1560
|
},
|
|
1412
|
-
visualConfig: { type: "object", required: false },
|
|
1413
|
-
sqlConfig: { type: "object", required: false },
|
|
1414
1561
|
timeout: { type: "number", required: false },
|
|
1415
1562
|
maxRows: { type: "number", required: false }
|
|
1416
1563
|
};
|
|
@@ -1419,11 +1566,11 @@ var BuildQueryContractSchema = {
|
|
|
1419
1566
|
var queryDefinition = {
|
|
1420
1567
|
id: "query",
|
|
1421
1568
|
name: "Query",
|
|
1422
|
-
description: "
|
|
1423
|
-
longDescription: "
|
|
1569
|
+
description: "Read records from a connected database, app or CRM",
|
|
1570
|
+
longDescription: "Pick a table or object, the fields you want and any filters, and the Query step reads the matching records. Filters can be grouped with AND / OR, compare two fields, or use a date window such as the last 30 days. You can also remove duplicates, count, total or average values by group, filter those totals, and sort the result. Works with databases (PostgreSQL, MySQL, SQL Server, MongoDB and more), apps (QuickBooks, HubSpot, Jira, Shopify, Google Sheets and more) and CRMs (Salesforce). The records it returns can be used by the steps that follow.",
|
|
1424
1571
|
category: "database" /* Database */,
|
|
1425
1572
|
type: "query" /* Query */,
|
|
1426
|
-
tags: ["query", "database", "
|
|
1573
|
+
tags: ["query", "database", "visual-builder", "no-code", "filter", "api", "crm", "built-in"],
|
|
1427
1574
|
status: "stable" /* Stable */,
|
|
1428
1575
|
icon: "QueryQueue",
|
|
1429
1576
|
color: "#0043ce",
|
|
@@ -1441,17 +1588,12 @@ var queryDefinition = {
|
|
|
1441
1588
|
key: "mode",
|
|
1442
1589
|
label: "Query Mode",
|
|
1443
1590
|
type: "select" /* Select */,
|
|
1444
|
-
description: "
|
|
1591
|
+
description: "Always 'visual' — the query is built with the visual builder",
|
|
1445
1592
|
options: [
|
|
1446
1593
|
{
|
|
1447
|
-
label: "Visual Builder
|
|
1594
|
+
label: "Visual Builder",
|
|
1448
1595
|
value: "visual" /* Visual */,
|
|
1449
|
-
description: "
|
|
1450
|
-
},
|
|
1451
|
-
{
|
|
1452
|
-
label: "SQL Editor (Advanced)",
|
|
1453
|
-
value: "sql" /* SQL */,
|
|
1454
|
-
description: "Write raw SQL queries with syntax highlighting"
|
|
1596
|
+
description: "Pick the table, fields and filters — no code needed"
|
|
1455
1597
|
}
|
|
1456
1598
|
],
|
|
1457
1599
|
defaultValue: "visual" /* Visual */,
|
|
@@ -1499,7 +1641,7 @@ var queryDefinition = {
|
|
|
1499
1641
|
key: "visualConfig.fields",
|
|
1500
1642
|
label: "Select Fields",
|
|
1501
1643
|
type: "array" /* Array */,
|
|
1502
|
-
description:
|
|
1644
|
+
description: 'The fields to return, as "table.column". Not needed for a summary query (aggregates / groupBy) or when every table and field is selected.',
|
|
1503
1645
|
validation: { required: true },
|
|
1504
1646
|
conditional: [
|
|
1505
1647
|
{
|
|
@@ -1542,39 +1684,6 @@ var queryDefinition = {
|
|
|
1542
1684
|
}
|
|
1543
1685
|
]
|
|
1544
1686
|
},
|
|
1545
|
-
{
|
|
1546
|
-
key: "sqlConfig.sql",
|
|
1547
|
-
label: "SQL Query",
|
|
1548
|
-
type: "code" /* Code */,
|
|
1549
|
-
language: "sql",
|
|
1550
|
-
description: "Write your SQL query here",
|
|
1551
|
-
placeholder: "SELECT * FROM users WHERE status = 'active' LIMIT 100",
|
|
1552
|
-
validation: { required: true },
|
|
1553
|
-
conditional: [
|
|
1554
|
-
{
|
|
1555
|
-
field: "mode",
|
|
1556
|
-
operator: "==",
|
|
1557
|
-
value: "sql" /* SQL */,
|
|
1558
|
-
action: "show"
|
|
1559
|
-
}
|
|
1560
|
-
]
|
|
1561
|
-
},
|
|
1562
|
-
{
|
|
1563
|
-
key: "sqlConfig.parameters",
|
|
1564
|
-
label: "Query Parameters",
|
|
1565
|
-
type: "json" /* JSON */,
|
|
1566
|
-
description: 'Parameters for parameterized queries (e.g., {"userId": 123})',
|
|
1567
|
-
validation: { required: false },
|
|
1568
|
-
advanced: true,
|
|
1569
|
-
conditional: [
|
|
1570
|
-
{
|
|
1571
|
-
field: "mode",
|
|
1572
|
-
operator: "==",
|
|
1573
|
-
value: "sql" /* SQL */,
|
|
1574
|
-
action: "show"
|
|
1575
|
-
}
|
|
1576
|
-
]
|
|
1577
|
-
},
|
|
1578
1687
|
{
|
|
1579
1688
|
key: "timeout",
|
|
1580
1689
|
label: "Query Timeout (ms)",
|
|
@@ -1588,7 +1697,7 @@ var queryDefinition = {
|
|
|
1588
1697
|
key: "maxRows",
|
|
1589
1698
|
label: "Max Rows",
|
|
1590
1699
|
type: "number" /* Number */,
|
|
1591
|
-
description: "Legacy step-level field —
|
|
1700
|
+
description: "Legacy step-level field — not read at run. To cap a run, set visualConfig.maxRows.",
|
|
1592
1701
|
defaultValue: 1000,
|
|
1593
1702
|
validation: { required: false, min: 1, max: 1e4 },
|
|
1594
1703
|
advanced: true
|
|
@@ -1604,7 +1713,7 @@ var queryDefinition = {
|
|
|
1604
1713
|
{
|
|
1605
1714
|
id: "query_mode",
|
|
1606
1715
|
label: "Query Configuration",
|
|
1607
|
-
description: "
|
|
1716
|
+
description: "The query is built with the visual builder",
|
|
1608
1717
|
fields: ["mode", "queryType"]
|
|
1609
1718
|
},
|
|
1610
1719
|
{
|
|
@@ -1613,12 +1722,6 @@ var queryDefinition = {
|
|
|
1613
1722
|
description: "Build your query visually without writing code",
|
|
1614
1723
|
fields: ["visualConfig.tables", "visualConfig.fields", "visualConfig.limit"]
|
|
1615
1724
|
},
|
|
1616
|
-
{
|
|
1617
|
-
id: "sql_editor",
|
|
1618
|
-
label: "SQL Editor",
|
|
1619
|
-
description: "Write raw SQL queries",
|
|
1620
|
-
fields: ["sqlConfig.sql", "sqlConfig.parameters"]
|
|
1621
|
-
},
|
|
1622
1725
|
{
|
|
1623
1726
|
id: "settings",
|
|
1624
1727
|
label: "Advanced Settings",
|
|
@@ -1658,7 +1761,7 @@ var queryDefinition = {
|
|
|
1658
1761
|
{
|
|
1659
1762
|
key: "generatedSQL",
|
|
1660
1763
|
type: "string" /* String */,
|
|
1661
|
-
description: "The
|
|
1764
|
+
description: "The query that ran, compiled from the visual builder",
|
|
1662
1765
|
example: "SELECT id, name, email FROM users WHERE status = 'active' LIMIT 100"
|
|
1663
1766
|
}
|
|
1664
1767
|
],
|
|
@@ -1784,6 +1887,32 @@ var aggregateDefinition = {
|
|
|
1784
1887
|
testable: false
|
|
1785
1888
|
};
|
|
1786
1889
|
|
|
1890
|
+
// src/catalog/storage/fileHelp.ts
|
|
1891
|
+
var FILE_OUTPUT_HELP = {
|
|
1892
|
+
operation: "The operation that ran",
|
|
1893
|
+
data: "File contents, for text files such as CSV, JSON or TXT",
|
|
1894
|
+
s3Key: "The file itself, for PDF, Excel, images and other non-text files. Pass it to Excel, CSV, IDP or an Upload step.",
|
|
1895
|
+
fileName: "File name",
|
|
1896
|
+
mimeType: "File type, e.g. text/csv or application/pdf",
|
|
1897
|
+
path: "Where the file was read from"
|
|
1898
|
+
};
|
|
1899
|
+
var FILE_INPUT_HELP = {
|
|
1900
|
+
uploadContent: "Leave blank to upload the file from the step before, or pick a value from an earlier step, or type the content.",
|
|
1901
|
+
formatContent: (format) => `The ${format} from an earlier step (a download or a generated file), or paste the ${format} here.`,
|
|
1902
|
+
excelContent: "The Excel file from an earlier step (a download or a generated file)."
|
|
1903
|
+
};
|
|
1904
|
+
var STORAGE_DOWNLOAD_OUTPUT_SCHEMA = {
|
|
1905
|
+
type: "object",
|
|
1906
|
+
properties: {
|
|
1907
|
+
operation: { type: "string", description: FILE_OUTPUT_HELP.operation },
|
|
1908
|
+
data: { type: "string", description: FILE_OUTPUT_HELP.data },
|
|
1909
|
+
s3Key: { type: "string", description: FILE_OUTPUT_HELP.s3Key },
|
|
1910
|
+
fileName: { type: "string", description: FILE_OUTPUT_HELP.fileName },
|
|
1911
|
+
mimeType: { type: "string", description: FILE_OUTPUT_HELP.mimeType },
|
|
1912
|
+
path: { type: "string", description: FILE_OUTPUT_HELP.path }
|
|
1913
|
+
}
|
|
1914
|
+
};
|
|
1915
|
+
|
|
1787
1916
|
// src/catalog/storage/csv-action/index.ts
|
|
1788
1917
|
var csvActionDefinition = {
|
|
1789
1918
|
id: "csv_action",
|
|
@@ -1798,7 +1927,7 @@ var csvActionDefinition = {
|
|
|
1798
1927
|
inputSchema: {
|
|
1799
1928
|
fields: [
|
|
1800
1929
|
{ key: "operation", label: "Operation", type: "select" /* Select */, options: [{ label: "Parse (CSV → Records)", value: "parse" }, { label: "Generate (Records → CSV)", value: "generate" }, { label: "Edit existing file", value: "edit" }], defaultValue: "parse", validation: { required: true } },
|
|
1801
|
-
{ key: "content", label: "CSV Content", type: "string" /* String */, description: "
|
|
1930
|
+
{ key: "content", label: "CSV Content", type: "string" /* String */, description: FILE_INPUT_HELP.formatContent("CSV") },
|
|
1802
1931
|
{ key: "delimiter", label: "Delimiter", type: "select" /* Select */, options: [{ label: "Comma (,)", value: "," }, { label: "Semicolon (;)", value: ";" }, { label: "Tab", value: "\t" }, { label: "Pipe (|)", value: "|" }], defaultValue: "," },
|
|
1803
1932
|
{ key: "headerRow", label: "Header Row", type: "number" /* Number */, description: "Row number containing column headers (default: 1)", defaultValue: 1 },
|
|
1804
1933
|
{ key: "encoding", label: "Encoding", type: "select" /* Select */, options: [{ label: "UTF-8", value: "utf-8" }, { label: "Latin-1", value: "latin1" }, { label: "ASCII", value: "ascii" }], defaultValue: "utf-8" },
|
|
@@ -1838,7 +1967,7 @@ var jsonActionDefinition = {
|
|
|
1838
1967
|
fields: [
|
|
1839
1968
|
{ key: "operation", label: "Operation", type: "select" /* Select */, options: [{ label: "Parse (JSON → Records)", value: "parse" }, { label: "Generate (Records → JSON)", value: "generate" }, { label: "Edit existing file", value: "edit" }], defaultValue: "parse", validation: { required: true } },
|
|
1840
1969
|
{ key: "format", label: "Format", type: "select" /* Select */, options: [{ label: "JSON (array)", value: "json" }, { label: "JSONL (one object per line)", value: "jsonl" }], defaultValue: "json" },
|
|
1841
|
-
{ key: "content", label: "JSON Content", type: "string" /* String */, description: "
|
|
1970
|
+
{ key: "content", label: "JSON Content", type: "string" /* String */, description: FILE_INPUT_HELP.formatContent("JSON") },
|
|
1842
1971
|
{ key: "prettyPrint", label: "Pretty Print", type: "boolean" /* Boolean */, description: "Format output with indentation", defaultValue: true },
|
|
1843
1972
|
{ key: "fileName", label: "Output File Name", type: "string" /* String */, defaultValue: "export.json" }
|
|
1844
1973
|
]
|
|
@@ -1874,7 +2003,7 @@ var xmlActionDefinition = {
|
|
|
1874
2003
|
inputSchema: {
|
|
1875
2004
|
fields: [
|
|
1876
2005
|
{ key: "operation", label: "Operation", type: "select" /* Select */, options: [{ label: "Parse (XML → Records)", value: "parse" }, { label: "Generate (Records → XML)", value: "generate" }, { label: "Edit existing file", value: "edit" }], defaultValue: "parse", validation: { required: true } },
|
|
1877
|
-
{ key: "content", label: "XML Content", type: "string" /* String */, description: "
|
|
2006
|
+
{ key: "content", label: "XML Content", type: "string" /* String */, description: FILE_INPUT_HELP.formatContent("XML") },
|
|
1878
2007
|
{ key: "rootElement", label: "Root Element", type: "string" /* String */, description: "Root XML element name (for generate)", defaultValue: "data" },
|
|
1879
2008
|
{ key: "rowElement", label: "Row Element", type: "string" /* String */, description: "Element name per record (for generate)", defaultValue: "row" },
|
|
1880
2009
|
{ key: "fileName", label: "Output File Name", type: "string" /* String */, defaultValue: "export.xml" }
|
|
@@ -1911,7 +2040,7 @@ var excelActionDefinition = {
|
|
|
1911
2040
|
inputSchema: {
|
|
1912
2041
|
fields: [
|
|
1913
2042
|
{ key: "operation", label: "Operation", type: "select" /* Select */, options: [{ label: "Parse (Excel → Records)", value: "parse" }, { label: "Generate (Records → Excel)", value: "generate" }, { label: "Edit existing file", value: "edit" }], defaultValue: "parse", validation: { required: true } },
|
|
1914
|
-
{ key: "content", label: "Excel File", type: "string" /* String */, description:
|
|
2043
|
+
{ key: "content", label: "Excel File", type: "string" /* String */, description: FILE_INPUT_HELP.excelContent },
|
|
1915
2044
|
{ key: "sheetName", label: "Sheet Name", type: "string" /* String */, description: "Name of sheet to read (default: first sheet)" },
|
|
1916
2045
|
{ key: "headerRow", label: "Header Row", type: "number" /* Number */, description: "Row number containing headers", defaultValue: 1 },
|
|
1917
2046
|
{ key: "fileName", label: "Output File Name", type: "string" /* String */, defaultValue: "export.xlsx" }
|
|
@@ -1923,7 +2052,7 @@ var excelActionDefinition = {
|
|
|
1923
2052
|
{ key: "data", type: "array" /* Array */, description: "Parsed records (parse mode) — loop these with a ForEach. File metadata in generate mode." },
|
|
1924
2053
|
{ key: "columns", type: "json" /* JSON */, description: "Column names" },
|
|
1925
2054
|
{ key: "rowCount", type: "number" /* Number */, description: "Number of records" },
|
|
1926
|
-
{ key: "s3Key", type: "string" /* String */, description: "
|
|
2055
|
+
{ key: "s3Key", type: "string" /* String */, description: "The generated Excel file. Pass it to an Upload step to save it." }
|
|
1927
2056
|
],
|
|
1928
2057
|
sampleOutput: { data: [{ id: 1, name: "Widget" }], columns: ["id", "name"], rowCount: 1 }
|
|
1929
2058
|
},
|
|
@@ -1953,7 +2082,7 @@ var s3ActionDefinition = {
|
|
|
1953
2082
|
{ key: "bucket", label: "Bucket Name", type: "string" /* String */, description: "S3 bucket name (defaults to the connector’s bucket)" },
|
|
1954
2083
|
{ key: "key", label: "Object Key (Path)", type: "string" /* String */, description: "S3 object key, e.g. data/reports/monthly.csv" },
|
|
1955
2084
|
{ key: "prefix", label: "Prefix (for List)", type: "string" /* String */, description: "Filter objects by prefix (folder path)" },
|
|
1956
|
-
{ key: "content", label: "File Content (for Upload)", type: "string" /* String */, description:
|
|
2085
|
+
{ key: "content", label: "File Content (for Upload)", type: "string" /* String */, description: FILE_INPUT_HELP.uploadContent },
|
|
1957
2086
|
{ key: "contentType", label: "Content Type", type: "string" /* String */, description: "MIME type (e.g. text/csv, application/json)", defaultValue: "application/octet-stream" },
|
|
1958
2087
|
{ key: "destBucket", label: "Destination Bucket (for Copy)", type: "string" /* String */ },
|
|
1959
2088
|
{ key: "destKey", label: "Destination Key (for Copy)", type: "string" /* String */ },
|
|
@@ -1994,7 +2123,7 @@ var ftpActionDefinition = {
|
|
|
1994
2123
|
{ key: "connector", label: "FTP / SFTP Connection", type: "connection" /* Connection */, description: "Select a configured FTP/SFTP connector from Integrations", validation: { required: true } },
|
|
1995
2124
|
{ key: "operation", label: "Operation", type: "select" /* Select */, options: [{ label: "Download File", value: "download" }, { label: "Upload File", value: "upload" }, { label: "List Files", value: "list" }, { label: "Delete File", value: "delete" }, { label: "Move / Rename", value: "move" }], defaultValue: "download", validation: { required: true } },
|
|
1996
2125
|
{ key: "remotePath", label: "Remote Path", type: "string" /* String */, description: "File or folder path on the server (use file explorer to browse)" },
|
|
1997
|
-
{ key: "content", label: "File Content (Upload)", type: "string" /* String */, description:
|
|
2126
|
+
{ key: "content", label: "File Content (Upload)", type: "string" /* String */, description: FILE_INPUT_HELP.uploadContent },
|
|
1998
2127
|
{ key: "fileName", label: "File Name (Upload)", type: "string" /* String */, description: "Name for the uploaded file" },
|
|
1999
2128
|
{ key: "destPath", label: "Destination Path (Move)", type: "string" /* String */, description: "New path for moved/renamed file" },
|
|
2000
2129
|
{ key: "ifExists", label: "If File Exists", type: "select" /* Select */, options: [{ label: "Overwrite", value: "overwrite" }, { label: "Skip", value: "skip" }], defaultValue: "overwrite" }
|
|
@@ -2035,7 +2164,7 @@ var googleDriveActionDefinition = {
|
|
|
2035
2164
|
{ key: "fileId", label: "File ID / Path", type: "string" /* String */, description: "Google Drive file ID (use file explorer to browse)" },
|
|
2036
2165
|
{ key: "folderId", label: "Folder ID", type: "string" /* String */, description: "Target folder (root if empty)" },
|
|
2037
2166
|
{ key: "fileName", label: "File Name", type: "string" /* String */ },
|
|
2038
|
-
{ key: "content", label: "File Content (Upload)", type: "string" /* String */, description:
|
|
2167
|
+
{ key: "content", label: "File Content (Upload)", type: "string" /* String */, description: FILE_INPUT_HELP.uploadContent },
|
|
2039
2168
|
{ key: "mimeType", label: "MIME Type", type: "string" /* String */, defaultValue: "application/octet-stream" },
|
|
2040
2169
|
{ key: "destFolderId", label: "Destination Folder (Move/Copy)", type: "string" /* String */ },
|
|
2041
2170
|
{ key: "shareEmail", label: "Share With Email", type: "string" /* String */ },
|
|
@@ -2077,7 +2206,7 @@ var dropboxActionDefinition = {
|
|
|
2077
2206
|
{ key: "connector", label: "Dropbox Account", type: "connection" /* Connection */, description: "Select a connected Dropbox account", validation: { required: true } },
|
|
2078
2207
|
{ key: "operation", label: "Operation", type: "select" /* Select */, options: [{ label: "Download File", value: "download" }, { label: "Upload File", value: "upload" }, { label: "List Files", value: "list" }, { label: "Delete", value: "delete" }, { label: "Move", value: "move" }, { label: "Copy", value: "copy" }, { label: "Create Folder", value: "create_folder" }, { label: "Get Shared Link", value: "shared_link" }], defaultValue: "download", validation: { required: true } },
|
|
2079
2208
|
{ key: "path", label: "File / Folder Path", type: "string" /* String */, description: "Path on Dropbox (use file explorer to browse)" },
|
|
2080
|
-
{ key: "content", label: "File Content (Upload)", type: "string" /* String */, description:
|
|
2209
|
+
{ key: "content", label: "File Content (Upload)", type: "string" /* String */, description: FILE_INPUT_HELP.uploadContent },
|
|
2081
2210
|
{ key: "fileName", label: "File Name (Upload)", type: "string" /* String */ },
|
|
2082
2211
|
{ key: "destPath", label: "Destination Path (Move/Copy)", type: "string" /* String */ },
|
|
2083
2212
|
{ key: "mode", label: "Upload Mode", type: "select" /* Select */, options: [{ label: "Add (fail if exists)", value: "add" }, { label: "Overwrite", value: "overwrite" }], defaultValue: "overwrite" }
|
|
@@ -2117,7 +2246,7 @@ var onedriveActionDefinition = {
|
|
|
2117
2246
|
{ key: "operation", label: "Operation", type: "select" /* Select */, options: [{ label: "Download File", value: "download" }, { label: "Upload File", value: "upload" }, { label: "List Files", value: "list" }, { label: "Delete", value: "delete" }, { label: "Move", value: "move" }, { label: "Copy", value: "copy" }], defaultValue: "download", validation: { required: true } },
|
|
2118
2247
|
{ key: "path", label: "File / Folder Path", type: "string" /* String */, description: "Path on OneDrive (use file explorer to browse)" },
|
|
2119
2248
|
{ key: "itemId", label: "Item ID", type: "string" /* String */, description: "OneDrive item ID (alternative to path)" },
|
|
2120
|
-
{ key: "content", label: "File Content (Upload)", type: "string" /* String */, description:
|
|
2249
|
+
{ key: "content", label: "File Content (Upload)", type: "string" /* String */, description: FILE_INPUT_HELP.uploadContent },
|
|
2121
2250
|
{ key: "fileName", label: "File Name (Upload)", type: "string" /* String */ },
|
|
2122
2251
|
{ key: "destPath", label: "Destination Path (Move/Copy)", type: "string" /* String */ }
|
|
2123
2252
|
]
|
|
@@ -2175,14 +2304,17 @@ var storageActionDefinition = {
|
|
|
2175
2304
|
},
|
|
2176
2305
|
{ key: "path", label: "Path", type: "string" /* String */, description: "File or folder path — use the file browser in the panel." },
|
|
2177
2306
|
{ key: "destPath", label: "Destination path", type: "string" /* String */, description: "Target for copy / move." },
|
|
2178
|
-
{ key: "content", label: "File content (for Upload)", type: "string" /* String */, description:
|
|
2307
|
+
{ key: "content", label: "File content (for Upload)", type: "string" /* String */, description: FILE_INPUT_HELP.uploadContent }
|
|
2179
2308
|
]
|
|
2180
2309
|
},
|
|
2181
2310
|
outputSchema: {
|
|
2182
2311
|
type: "object",
|
|
2183
2312
|
properties: [
|
|
2184
2313
|
{ key: "operation", type: "string" /* String */, description: "The operation that ran" },
|
|
2185
|
-
{ key: "data", type: "json" /* JSON */, description:
|
|
2314
|
+
{ key: "data", type: "json" /* JSON */, description: `Download: ${FILE_OUTPUT_HELP.data}. Other operations: their result.` },
|
|
2315
|
+
{ key: "s3Key", type: "string" /* String */, description: `Download: ${FILE_OUTPUT_HELP.s3Key}` },
|
|
2316
|
+
{ key: "fileName", type: "string" /* String */, description: `Download: ${FILE_OUTPUT_HELP.fileName}` },
|
|
2317
|
+
{ key: "mimeType", type: "string" /* String */, description: `Download: ${FILE_OUTPUT_HELP.mimeType}` },
|
|
2186
2318
|
{ key: "files", type: "json" /* JSON */, description: "Listing (for the List operation)" },
|
|
2187
2319
|
{ key: "path", type: "string" /* String */, description: "Affected file/folder path" },
|
|
2188
2320
|
{ key: "url", type: "string" /* String */, description: "Signed URL (for the Get signed URL operation)" }
|
|
@@ -2824,6 +2956,10 @@ function isSatisfiedOutOfBand(actionId, fieldKey, config) {
|
|
|
2824
2956
|
return true;
|
|
2825
2957
|
return Array.isArray(config.blocks) && config.blocks.length > 0;
|
|
2826
2958
|
}
|
|
2959
|
+
if (actionId === "query" && fieldKey === "visualConfig.fields")
|
|
2960
|
+
return queryFieldsSatisfied(config);
|
|
2961
|
+
if (actionId === "query" && fieldKey === "visualConfig.tables")
|
|
2962
|
+
return queryTablesSatisfied(config);
|
|
2827
2963
|
return false;
|
|
2828
2964
|
}
|
|
2829
2965
|
var MESSAGE_BODY_ACTIONS = new Set([
|
|
@@ -3718,9 +3854,6 @@ var EmailConfig = {
|
|
|
3718
3854
|
}
|
|
3719
3855
|
}
|
|
3720
3856
|
};
|
|
3721
|
-
// src/catalog/isOneOf.ts
|
|
3722
|
-
var isOneOf = (set, value) => typeof value === "string" && set.includes(value);
|
|
3723
|
-
|
|
3724
3857
|
// src/catalog/general/transform/config.ts
|
|
3725
3858
|
var TRANSFORM_MODES = ["visual", "expression"];
|
|
3726
3859
|
var TRANSFORM_OPERATIONS = [
|
|
@@ -4628,10 +4761,10 @@ var FILE_EDIT_SCHEMA = {
|
|
|
4628
4761
|
matchOp: { type: "select", required: false, options: FILE_EDIT_MATCH_OPS, description: 'set_fields: comparison; "=" by default' },
|
|
4629
4762
|
matchValue: { type: "string", required: false, description: "set_fields: value compared against" },
|
|
4630
4763
|
setFields: { type: "object", required: false, description: "set_fields: { column: newValue } applied to matching rows" },
|
|
4631
|
-
destination: { type: "select", required: false, options: FILE_EDIT_DESTINATIONS, description: '"new" writes an edited copy (default); "in_place" overwrites
|
|
4764
|
+
destination: { type: "select", required: false, options: FILE_EDIT_DESTINATIONS, description: '"new" writes an edited copy (default); "in_place" overwrites the source file' },
|
|
4632
4765
|
outputFileName: { type: "string", required: false, description: 'Name of the edited copy when destination is "new"' },
|
|
4633
4766
|
source: { type: "select", required: false, options: FILE_EDIT_SOURCES, description: "Where the file to edit comes from; inferred from content when absent" },
|
|
4634
|
-
s3Key: { type: "string", required: false, description:
|
|
4767
|
+
s3Key: { type: "string", required: false, description: `source "s3": an earlier step's file (its s3Key output) to edit` },
|
|
4635
4768
|
url: { type: "url", required: false, description: 'source "url": the file to edit is fetched from here' }
|
|
4636
4769
|
};
|
|
4637
4770
|
function fileEditErrors(config) {
|
|
@@ -4663,7 +4796,7 @@ var CsvActionConfig = {
|
|
|
4663
4796
|
},
|
|
4664
4797
|
schema: {
|
|
4665
4798
|
operation: { type: "select", required: true, options: FILE_FORMAT_OPERATIONS, description: "parse (CSV → records), generate (records → CSV) or edit an existing file" },
|
|
4666
|
-
content: { type: "text", required: false, description: "CSV text, or
|
|
4799
|
+
content: { type: "text", required: false, description: "CSV text, or a reference to an earlier step's file content" },
|
|
4667
4800
|
delimiter: { type: "select", required: false, options: [",", ";", "\t", "|"] },
|
|
4668
4801
|
headerRow: { type: "number", required: false, min: 1, description: "1-based row that holds the column headers" },
|
|
4669
4802
|
encoding: { type: "string", required: false, description: "Text encoding (utf-8 by default)" },
|
|
@@ -4689,7 +4822,7 @@ var JsonActionConfig = {
|
|
|
4689
4822
|
schema: {
|
|
4690
4823
|
operation: { type: "select", required: true, options: FILE_FORMAT_OPERATIONS, description: "parse (JSON → records), generate (records → JSON) or edit an existing file" },
|
|
4691
4824
|
format: { type: "select", required: false, options: ["json", "jsonl"], description: "json (array) or jsonl (one object per line)" },
|
|
4692
|
-
content: { type: "text", required: false, description: "JSON text, or
|
|
4825
|
+
content: { type: "text", required: false, description: "JSON text, or a reference to an earlier step's file content" },
|
|
4693
4826
|
prettyPrint: { type: "boolean", required: false, description: "generate: indent the output" },
|
|
4694
4827
|
fileName: { type: "string", required: false, description: "generate: name of the produced file" },
|
|
4695
4828
|
jsonl: { type: "boolean", required: false, description: "edit: treat the file as JSONL" },
|
|
@@ -4716,7 +4849,7 @@ var XmlActionConfig = {
|
|
|
4716
4849
|
},
|
|
4717
4850
|
schema: {
|
|
4718
4851
|
operation: { type: "select", required: true, options: FILE_FORMAT_OPERATIONS, description: "parse (XML → records), generate (records → XML) or edit an existing file" },
|
|
4719
|
-
content: { type: "text", required: false, description: "XML text, or
|
|
4852
|
+
content: { type: "text", required: false, description: "XML text, or a reference to an earlier step's file content" },
|
|
4720
4853
|
rootElement: { type: "string", required: false, description: "generate: root element name" },
|
|
4721
4854
|
rowElement: { type: "string", required: false, description: "Element that holds one record" },
|
|
4722
4855
|
fileName: { type: "string", required: false, description: "generate: name of the produced file" },
|
|
@@ -4738,7 +4871,7 @@ var ExcelActionConfig = {
|
|
|
4738
4871
|
},
|
|
4739
4872
|
schema: {
|
|
4740
4873
|
operation: { type: "select", required: true, options: FILE_FORMAT_OPERATIONS, description: "parse (Excel → records), generate (records → Excel) or edit an existing file" },
|
|
4741
|
-
content: { type: "text", required: false, description: "
|
|
4874
|
+
content: { type: "text", required: false, description: "A reference to an earlier step's Excel file (its s3Key output), or base64 bytes" },
|
|
4742
4875
|
sheetName: { type: "string", required: false, description: "Sheet to read; the first sheet when absent" },
|
|
4743
4876
|
headerRow: { type: "number", required: false, min: 1, description: "1-based row that holds the column headers" },
|
|
4744
4877
|
fileName: { type: "string", required: false, description: "generate: name of the produced file" },
|
|
@@ -4756,7 +4889,7 @@ var FIXED_SCHEMA = {
|
|
|
4756
4889
|
connector: { type: "connection", required: true, description: "Storage connector instance; its provider decides the operations" },
|
|
4757
4890
|
operation: { type: "select", required: true, description: "Per provider: list, download, upload, delete, copy, move, presign, share, create_folder, transfer, sync" },
|
|
4758
4891
|
outputAs: { type: "string", required: false, description: "Name of this step's output in the run's data bag" },
|
|
4759
|
-
content: { type: "text", required: false, description: "upload: the
|
|
4892
|
+
content: { type: "text", required: false, description: "upload: the file content; blank uploads the file the step before produced" },
|
|
4760
4893
|
destConnector: { type: "connection", required: false, description: "transfer / sync: destination connector instance" },
|
|
4761
4894
|
destPath: { type: "string", required: false, description: "transfer / sync: destination path; also move / copy destination on path-based providers" },
|
|
4762
4895
|
sourceIsDirectory: { type: "boolean", required: false, description: "transfer: copy a folder recursively" },
|