@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.js
CHANGED
|
@@ -1022,32 +1022,51 @@ var QueryType;
|
|
|
1022
1022
|
QueryType["Delete"] = "delete";
|
|
1023
1023
|
QueryType["Custom"] = "custom";
|
|
1024
1024
|
})(QueryType ||= {});
|
|
1025
|
-
var
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1025
|
+
var QUERY_MODES = ["visual" /* Visual */];
|
|
1026
|
+
var QUERY_TYPES = ["select" /* Select */];
|
|
1027
|
+
var QUERY_COMPARISON_OPERATORS = [
|
|
1028
|
+
"=",
|
|
1029
|
+
"!=",
|
|
1030
|
+
">",
|
|
1031
|
+
"<",
|
|
1032
|
+
">=",
|
|
1033
|
+
"<=",
|
|
1034
|
+
"equals",
|
|
1035
|
+
"notEquals",
|
|
1036
|
+
"greaterThan",
|
|
1037
|
+
"lessThan",
|
|
1038
|
+
"greaterOrEqual",
|
|
1039
|
+
"lessOrEqual"
|
|
1040
|
+
];
|
|
1041
|
+
var QUERY_RELATIVE_DATE_OPERATORS = ["inRelative", "notInRelative"];
|
|
1042
|
+
var QUERY_CONDITION_OPERATORS = [
|
|
1043
|
+
...QUERY_COMPARISON_OPERATORS,
|
|
1044
|
+
"contains",
|
|
1045
|
+
"startsWith",
|
|
1046
|
+
"endsWith",
|
|
1047
|
+
"like",
|
|
1048
|
+
"notLike",
|
|
1049
|
+
"in",
|
|
1050
|
+
"notIn",
|
|
1051
|
+
"between",
|
|
1052
|
+
"isNull",
|
|
1053
|
+
"isNotNull",
|
|
1054
|
+
...QUERY_RELATIVE_DATE_OPERATORS
|
|
1055
|
+
];
|
|
1056
|
+
var RELATIVE_DATE_DIRECTIONS = ["last", "next", "this"];
|
|
1057
|
+
var RELATIVE_DATE_UNITS = ["day", "week", "month", "quarter", "year"];
|
|
1058
|
+
var RELATIVE_DATE_MAX_AMOUNT = 3650;
|
|
1059
|
+
var MAX_CONDITION_DEPTH = 3;
|
|
1060
|
+
var isConditionGroup = (c) => !!c && typeof c === "object" && c.type === "group";
|
|
1061
|
+
var QUERY_AGGREGATE_FUNCTIONS = ["COUNT", "COUNT_DISTINCT", "SUM", "AVG", "MIN", "MAX"];
|
|
1030
1062
|
var SortDirection;
|
|
1031
1063
|
((SortDirection) => {
|
|
1032
1064
|
SortDirection["Ascending"] = "asc";
|
|
1033
1065
|
SortDirection["Descending"] = "desc";
|
|
1034
1066
|
})(SortDirection ||= {});
|
|
1035
|
-
|
|
1036
|
-
(
|
|
1037
|
-
|
|
1038
|
-
JoinType["Left"] = "left";
|
|
1039
|
-
JoinType["Right"] = "right";
|
|
1040
|
-
JoinType["Full"] = "full";
|
|
1041
|
-
})(JoinType ||= {});
|
|
1042
|
-
var AggregateFunction;
|
|
1043
|
-
((AggregateFunction) => {
|
|
1044
|
-
AggregateFunction["Count"] = "count";
|
|
1045
|
-
AggregateFunction["Sum"] = "sum";
|
|
1046
|
-
AggregateFunction["Average"] = "avg";
|
|
1047
|
-
AggregateFunction["Min"] = "min";
|
|
1048
|
-
AggregateFunction["Max"] = "max";
|
|
1049
|
-
AggregateFunction["CountDistinct"] = "count_distinct";
|
|
1050
|
-
})(AggregateFunction ||= {});
|
|
1067
|
+
// src/catalog/isOneOf.ts
|
|
1068
|
+
var isOneOf = (set, value) => typeof value === "string" && set.includes(value);
|
|
1069
|
+
|
|
1051
1070
|
// src/catalog/general/query/config.ts
|
|
1052
1071
|
var BuildQueryConfigDefaults = {
|
|
1053
1072
|
mode: "visual" /* Visual */,
|
|
@@ -1057,160 +1076,262 @@ var BuildQueryConfigDefaults = {
|
|
|
1057
1076
|
visualConfig: {
|
|
1058
1077
|
tables: [],
|
|
1059
1078
|
fields: [],
|
|
1060
|
-
|
|
1079
|
+
conditions: [],
|
|
1061
1080
|
joins: [],
|
|
1062
1081
|
groupBy: [],
|
|
1082
|
+
aggregates: [],
|
|
1063
1083
|
sort: [],
|
|
1064
|
-
limit: 10
|
|
1065
|
-
offset: 0
|
|
1066
|
-
},
|
|
1067
|
-
sqlConfig: {
|
|
1068
|
-
sql: "",
|
|
1069
|
-
parameters: {}
|
|
1084
|
+
limit: 10
|
|
1070
1085
|
}
|
|
1071
1086
|
};
|
|
1072
|
-
var
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1087
|
+
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.";
|
|
1088
|
+
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.";
|
|
1089
|
+
var err = (field, message) => ({ field, message, severity: "error" });
|
|
1090
|
+
var isRecord = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
1091
|
+
var nonEmptyString = (v) => typeof v === "string" && v.trim().length > 0;
|
|
1092
|
+
var hasValue = (v) => v !== undefined && v !== null && !(typeof v === "string" && v.trim() === "") && !(Array.isArray(v) && v.length === 0);
|
|
1093
|
+
var arr = (v) => Array.isArray(v) ? v : [];
|
|
1094
|
+
function queryFieldsSatisfied(config) {
|
|
1095
|
+
const vc = isRecord(config?.visualConfig) ? config.visualConfig : undefined;
|
|
1096
|
+
if (!vc)
|
|
1097
|
+
return false;
|
|
1098
|
+
if (vc.selectAll === true)
|
|
1099
|
+
return true;
|
|
1100
|
+
if (arr(vc.fields).length > 0)
|
|
1101
|
+
return true;
|
|
1102
|
+
if (arr(vc.aggregates).length > 0 || arr(vc.groupBy).length > 0)
|
|
1103
|
+
return true;
|
|
1104
|
+
const sql = isRecord(config?.sqlConfig) ? config.sqlConfig.sql : undefined;
|
|
1105
|
+
return arr(vc.tables).length > 0 && nonEmptyString(sql);
|
|
1106
|
+
}
|
|
1107
|
+
function queryTablesSatisfied(config) {
|
|
1108
|
+
const vc = isRecord(config?.visualConfig) ? config.visualConfig : undefined;
|
|
1109
|
+
if (!vc)
|
|
1110
|
+
return false;
|
|
1111
|
+
return vc.selectAll === true || arr(vc.tables).length > 0;
|
|
1112
|
+
}
|
|
1113
|
+
var OPERATOR_LIST = QUERY_CONDITION_OPERATORS.join(", ");
|
|
1114
|
+
function validateRelative(path, relative) {
|
|
1115
|
+
if (!isRecord(relative)) {
|
|
1116
|
+
return [err(`${path}.relative`, `${path}: a relative-date condition needs relative, e.g. { direction: 'last', amount: 30, unit: 'day' }`)];
|
|
1081
1117
|
}
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
severity: "error"
|
|
1087
|
-
});
|
|
1118
|
+
const issues = [];
|
|
1119
|
+
const { direction, unit, amount, includeCurrent } = relative;
|
|
1120
|
+
if (!isOneOf(RELATIVE_DATE_DIRECTIONS, direction)) {
|
|
1121
|
+
issues.push(err(`${path}.relative.direction`, `${path}.relative.direction must be one of: ${RELATIVE_DATE_DIRECTIONS.join(", ")} (got "${String(direction)}")`));
|
|
1088
1122
|
}
|
|
1089
|
-
if (!
|
|
1090
|
-
|
|
1091
|
-
field: "queryType",
|
|
1092
|
-
message: "Query type is required",
|
|
1093
|
-
severity: "error"
|
|
1094
|
-
});
|
|
1123
|
+
if (!isOneOf(RELATIVE_DATE_UNITS, unit)) {
|
|
1124
|
+
issues.push(err(`${path}.relative.unit`, `${path}.relative.unit must be one of: ${RELATIVE_DATE_UNITS.join(", ")} (got "${String(unit)}")`));
|
|
1095
1125
|
}
|
|
1096
|
-
if (
|
|
1097
|
-
const
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
errors.push(...sqlErrors);
|
|
1126
|
+
if (direction === "last" || direction === "next") {
|
|
1127
|
+
const ok = typeof amount === "number" && Number.isInteger(amount) && amount >= 1 && amount <= RELATIVE_DATE_MAX_AMOUNT;
|
|
1128
|
+
if (!ok) {
|
|
1129
|
+
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' })`));
|
|
1130
|
+
}
|
|
1102
1131
|
}
|
|
1103
|
-
if (
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1132
|
+
if (includeCurrent !== undefined && typeof includeCurrent !== "boolean") {
|
|
1133
|
+
issues.push(err(`${path}.relative.includeCurrent`, `${path}.relative.includeCurrent must be true or false`));
|
|
1134
|
+
}
|
|
1135
|
+
return issues;
|
|
1136
|
+
}
|
|
1137
|
+
function validateRow(path, row, inHaving) {
|
|
1138
|
+
const issues = [];
|
|
1139
|
+
if (!nonEmptyString(row.field)) {
|
|
1140
|
+
issues.push(err(`${path}.field`, `${path}: choose the field to compare (field is empty)`));
|
|
1141
|
+
}
|
|
1142
|
+
if (!isOneOf(QUERY_CONDITION_OPERATORS, row.operator)) {
|
|
1143
|
+
issues.push(err(`${path}.operator`, `${path}.operator must be one of: ${OPERATOR_LIST} (got "${String(row.operator)}")`));
|
|
1144
|
+
return issues;
|
|
1145
|
+
}
|
|
1146
|
+
const op = row.operator;
|
|
1147
|
+
const isRelative = isOneOf(QUERY_RELATIVE_DATE_OPERATORS, op);
|
|
1148
|
+
const comparesField = row.valueField !== undefined && row.valueField !== null && row.valueField !== "";
|
|
1149
|
+
if (isRelative) {
|
|
1150
|
+
if (inHaving) {
|
|
1151
|
+
issues.push(err(`${path}.operator`, `${path}: '${op}' filters dates on rows — put it in conditions, not having`));
|
|
1152
|
+
} else {
|
|
1153
|
+
issues.push(...validateRelative(path, row.relative));
|
|
1154
|
+
}
|
|
1155
|
+
if (hasValue(row.value) || comparesField) {
|
|
1156
|
+
issues.push(err(path, `${path}: a relative-date condition takes its window from relative — remove value and valueField`));
|
|
1116
1157
|
}
|
|
1158
|
+
} else if (row.relative !== undefined) {
|
|
1159
|
+
issues.push(err(`${path}.relative`, `${path}: relative is only used with 'inRelative' or 'notInRelative' — change the operator or remove relative`));
|
|
1117
1160
|
}
|
|
1118
|
-
if (
|
|
1119
|
-
if (
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
});
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
message: "Max rows is very high (>10,000). This may impact performance.",
|
|
1129
|
-
severity: "warning"
|
|
1130
|
-
});
|
|
1161
|
+
if (comparesField) {
|
|
1162
|
+
if (inHaving) {
|
|
1163
|
+
issues.push(err(`${path}.valueField`, `${path}: having compares with a value — use value, not valueField`));
|
|
1164
|
+
} else if (!nonEmptyString(row.valueField)) {
|
|
1165
|
+
issues.push(err(`${path}.valueField`, `${path}.valueField must name the field to compare with`));
|
|
1166
|
+
} else if (!isOneOf(QUERY_COMPARISON_OPERATORS, op)) {
|
|
1167
|
+
issues.push(err(`${path}.valueField`, `${path}: comparing with another field works with =, !=, >, <, >=, <= only (got "${op}")`));
|
|
1168
|
+
}
|
|
1169
|
+
if (hasValue(row.value)) {
|
|
1170
|
+
issues.push(err(path, `${path}: set either value (compare with a value) or valueField (compare with another field), not both`));
|
|
1131
1171
|
}
|
|
1132
1172
|
}
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1173
|
+
if (row.joinOperator !== undefined && !(typeof row.joinOperator === "string" && /^(and|or)$/i.test(row.joinOperator))) {
|
|
1174
|
+
issues.push(err(`${path}.joinOperator`, `${path}.joinOperator must be AND or OR (got "${String(row.joinOperator)}")`));
|
|
1175
|
+
}
|
|
1176
|
+
return issues;
|
|
1177
|
+
}
|
|
1178
|
+
function validateConditionList(path, list, depth) {
|
|
1179
|
+
if (list === undefined || list === null)
|
|
1180
|
+
return [];
|
|
1181
|
+
if (!Array.isArray(list))
|
|
1182
|
+
return [err(path, `${path} must be a list of conditions`)];
|
|
1183
|
+
const issues = [];
|
|
1184
|
+
list.forEach((item, i) => {
|
|
1185
|
+
const at = `${path}[${i}]`;
|
|
1186
|
+
if (!isRecord(item)) {
|
|
1187
|
+
issues.push(err(at, `${at} must be a condition ({ field, operator, value }) or a group ({ type: 'group', conditions })`));
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
if (isConditionGroup(item)) {
|
|
1191
|
+
if (depth + 1 > MAX_CONDITION_DEPTH) {
|
|
1192
|
+
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`));
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
if (item.joinOperator !== undefined && !(typeof item.joinOperator === "string" && /^(and|or)$/i.test(item.joinOperator))) {
|
|
1196
|
+
issues.push(err(`${at}.joinOperator`, `${at}.joinOperator must be AND or OR (got "${String(item.joinOperator)}")`));
|
|
1197
|
+
}
|
|
1198
|
+
if (!Array.isArray(item.conditions) || item.conditions.length === 0) {
|
|
1199
|
+
issues.push(err(`${at}.conditions`, `${at}: a group needs at least one condition — add one or remove the group`));
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
issues.push(...validateConditionList(`${at}.conditions`, item.conditions, depth + 1));
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
issues.push(...validateRow(at, item, false));
|
|
1206
|
+
});
|
|
1207
|
+
return issues;
|
|
1208
|
+
}
|
|
1209
|
+
function validateAggregates(vc) {
|
|
1210
|
+
const list = vc.aggregates;
|
|
1211
|
+
if (list === undefined || list === null)
|
|
1212
|
+
return [];
|
|
1213
|
+
if (!Array.isArray(list))
|
|
1214
|
+
return [err("visualConfig.aggregates", "visualConfig.aggregates must be a list of { fn, field, alias }")];
|
|
1215
|
+
const issues = [];
|
|
1216
|
+
list.forEach((a, i) => {
|
|
1217
|
+
const at = `visualConfig.aggregates[${i}]`;
|
|
1218
|
+
if (!isRecord(a)) {
|
|
1219
|
+
issues.push(err(at, `${at} must be { fn, field, alias }`));
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
const fn = typeof a.fn === "string" ? a.fn.toUpperCase() : a.fn;
|
|
1223
|
+
if (!isOneOf(QUERY_AGGREGATE_FUNCTIONS, fn)) {
|
|
1224
|
+
issues.push(err(`${at}.fn`, `${at}.fn must be one of: ${QUERY_AGGREGATE_FUNCTIONS.join(", ")} (got "${String(a.fn)}")`));
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
const allRows = !nonEmptyString(a.field) || a.field.trim() === "*";
|
|
1228
|
+
if (allRows && fn !== "COUNT") {
|
|
1229
|
+
issues.push(err(`${at}.field`, `${at}: ${fn} needs a field to work on — set field to a column name`));
|
|
1230
|
+
}
|
|
1231
|
+
if (a.alias !== undefined && a.alias !== null && !nonEmptyString(a.alias)) {
|
|
1232
|
+
issues.push(err(`${at}.alias`, `${at}.alias must be a name for the result column, or left out`));
|
|
1233
|
+
}
|
|
1234
|
+
});
|
|
1235
|
+
return issues;
|
|
1236
|
+
}
|
|
1237
|
+
function validateHaving(vc) {
|
|
1238
|
+
const list = vc.having;
|
|
1239
|
+
if (list === undefined || list === null || Array.isArray(list) && list.length === 0)
|
|
1240
|
+
return [];
|
|
1241
|
+
if (!Array.isArray(list))
|
|
1242
|
+
return [err("visualConfig.having", "visualConfig.having must be a list of conditions")];
|
|
1243
|
+
const aggregates = arr(vc.aggregates).filter(isRecord);
|
|
1244
|
+
const groupBy = arr(vc.groupBy).filter(nonEmptyString);
|
|
1245
|
+
if (aggregates.length === 0 && groupBy.length === 0) {
|
|
1246
|
+
return [err("visualConfig.having", "visualConfig.having filters grouped results, so it needs aggregates or groupBy — add them, or move these filters to conditions")];
|
|
1247
|
+
}
|
|
1248
|
+
const names = new Set([
|
|
1249
|
+
...groupBy,
|
|
1250
|
+
...aggregates.map((a) => a.alias).filter(nonEmptyString)
|
|
1251
|
+
]);
|
|
1252
|
+
const issues = [];
|
|
1253
|
+
list.forEach((row, i) => {
|
|
1254
|
+
const at = `visualConfig.having[${i}]`;
|
|
1255
|
+
if (!isRecord(row) || isConditionGroup(row)) {
|
|
1256
|
+
issues.push(err(at, `${at} must be a single condition ({ field, operator, value }); groups are not used in having`));
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
issues.push(...validateRow(at, row, true));
|
|
1260
|
+
if (nonEmptyString(row.field) && !names.has(row.field)) {
|
|
1261
|
+
const known = [...names];
|
|
1262
|
+
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)")));
|
|
1263
|
+
}
|
|
1264
|
+
});
|
|
1265
|
+
return issues;
|
|
1266
|
+
}
|
|
1267
|
+
function validateVisualConfig(config) {
|
|
1268
|
+
const vc = config.visualConfig;
|
|
1269
|
+
if (!isRecord(vc)) {
|
|
1270
|
+
return [err("visualConfig", "visualConfig is required — describe the query with tables (what to read), fields (which columns) and optional conditions")];
|
|
1271
|
+
}
|
|
1272
|
+
const issues = [];
|
|
1273
|
+
if (!queryTablesSatisfied(config)) {
|
|
1274
|
+
issues.push(err("visualConfig.tables", "At least one table must be selected (visualConfig.tables)"));
|
|
1275
|
+
}
|
|
1276
|
+
if (!queryFieldsSatisfied(config)) {
|
|
1277
|
+
issues.push(err("visualConfig.fields", "At least one field must be selected (visualConfig.fields), or add aggregates / groupBy for a summary query"));
|
|
1278
|
+
}
|
|
1279
|
+
if (arr(vc.tables).length > 1 && arr(vc.joins).length === 0) {
|
|
1280
|
+
issues.push(err("visualConfig.joins", "Joins are required when selecting from multiple tables (visualConfig.joins)"));
|
|
1281
|
+
}
|
|
1282
|
+
if (vc.distinct !== undefined && typeof vc.distinct !== "boolean") {
|
|
1283
|
+
issues.push(err("visualConfig.distinct", "visualConfig.distinct must be true or false"));
|
|
1284
|
+
}
|
|
1285
|
+
issues.push(...validateConditionList("visualConfig.conditions", vc.conditions, 0));
|
|
1286
|
+
issues.push(...validateAggregates(vc));
|
|
1287
|
+
issues.push(...validateHaving(vc));
|
|
1288
|
+
return issues;
|
|
1289
|
+
}
|
|
1290
|
+
var validateBuildQueryConfig = (config) => {
|
|
1291
|
+
const c = config;
|
|
1140
1292
|
const errors = [];
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
message: "Visual query configuration is required",
|
|
1145
|
-
severity: "error"
|
|
1146
|
-
});
|
|
1147
|
-
return errors;
|
|
1293
|
+
const warnings = [];
|
|
1294
|
+
if (!c.connectorId) {
|
|
1295
|
+
errors.push(err("connectorId", "Please select a connector (connectorId)"));
|
|
1148
1296
|
}
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1297
|
+
const mode = c.mode;
|
|
1298
|
+
const visual = mode === undefined || mode === null || mode === "" || isOneOf(QUERY_MODES, mode);
|
|
1299
|
+
if (!visual) {
|
|
1300
|
+
errors.push(err("mode", `Mode must be one of: ${QUERY_MODES.join(", ")} (got "${String(mode)}"). ${QUERY_VISUAL_ONLY_MESSAGE}`));
|
|
1301
|
+
} else if (!mode) {
|
|
1302
|
+
errors.push(err("mode", "Query mode is required — set mode to 'visual'"));
|
|
1155
1303
|
}
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
});
|
|
1304
|
+
const queryType = c.queryType;
|
|
1305
|
+
if (!queryType) {
|
|
1306
|
+
errors.push(err("queryType", "Query type is required — set queryType to 'select'"));
|
|
1307
|
+
} else if (!isOneOf(QUERY_TYPES, queryType)) {
|
|
1308
|
+
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."));
|
|
1162
1309
|
}
|
|
1163
|
-
if (
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
});
|
|
1310
|
+
if (visual) {
|
|
1311
|
+
const sql = isRecord(c.sqlConfig) ? c.sqlConfig.sql : undefined;
|
|
1312
|
+
if (nonEmptyString(sql) && !isRecord(c.visualConfig)) {
|
|
1313
|
+
errors.push(err("sqlConfig.sql", QUERY_AUTHORED_SQL_MESSAGE));
|
|
1314
|
+
} else {
|
|
1315
|
+
errors.push(...validateVisualConfig(c));
|
|
1170
1316
|
}
|
|
1171
1317
|
}
|
|
1172
|
-
const
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
message: "Group by is required when mixing aggregate and non-aggregate fields",
|
|
1179
|
-
severity: "error"
|
|
1180
|
-
});
|
|
1318
|
+
const timeout = c.timeout;
|
|
1319
|
+
if (typeof timeout === "number") {
|
|
1320
|
+
if (timeout < 1000) {
|
|
1321
|
+
errors.push(err("timeout", "Timeout must be at least 1000ms"));
|
|
1322
|
+
} else if (timeout > 300000) {
|
|
1323
|
+
warnings.push({ field: "timeout", message: "Timeout is very high (>5 minutes)", severity: "warning" });
|
|
1181
1324
|
}
|
|
1182
1325
|
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
field: "sqlConfig",
|
|
1190
|
-
message: "SQL query configuration is required",
|
|
1191
|
-
severity: "error"
|
|
1192
|
-
});
|
|
1193
|
-
return errors;
|
|
1194
|
-
}
|
|
1195
|
-
if (!config.sql || config.sql.trim().length === 0) {
|
|
1196
|
-
errors.push({
|
|
1197
|
-
field: "sqlConfig.sql",
|
|
1198
|
-
message: "SQL query is required",
|
|
1199
|
-
severity: "error"
|
|
1200
|
-
});
|
|
1201
|
-
} else {
|
|
1202
|
-
const sql = config.sql.trim().toLowerCase();
|
|
1203
|
-
const dangerousKeywords = ["drop", "truncate", "alter", "create", "grant", "revoke"];
|
|
1204
|
-
const hasDangerousKeyword = dangerousKeywords.some((keyword) => sql.includes(keyword + " "));
|
|
1205
|
-
if (hasDangerousKeyword) {
|
|
1206
|
-
errors.push({
|
|
1207
|
-
field: "sqlConfig.sql",
|
|
1208
|
-
message: "SQL query contains potentially dangerous operations (DROP, TRUNCATE, ALTER, etc.)",
|
|
1209
|
-
severity: "error"
|
|
1210
|
-
});
|
|
1326
|
+
const maxRows = c.maxRows;
|
|
1327
|
+
if (typeof maxRows === "number") {
|
|
1328
|
+
if (maxRows < 1) {
|
|
1329
|
+
errors.push(err("maxRows", "Max rows must be at least 1"));
|
|
1330
|
+
} else if (maxRows > 1e4) {
|
|
1331
|
+
warnings.push({ field: "maxRows", message: "Max rows is very high (>10,000). This may impact performance.", severity: "warning" });
|
|
1211
1332
|
}
|
|
1212
1333
|
}
|
|
1213
|
-
return errors;
|
|
1334
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
1214
1335
|
};
|
|
1215
1336
|
function isQueryableConnector(connector) {
|
|
1216
1337
|
if (connector.isQueryable !== undefined) {
|
|
@@ -1242,17 +1363,26 @@ var BuildQueryContractSchema = {
|
|
|
1242
1363
|
mode: {
|
|
1243
1364
|
type: "select",
|
|
1244
1365
|
required: true,
|
|
1245
|
-
options:
|
|
1246
|
-
description: "visual: the
|
|
1366
|
+
options: QUERY_MODES,
|
|
1367
|
+
description: "Always 'visual': the Query step is built with the visual builder and described in visualConfig. Hand-written SQL is not accepted."
|
|
1247
1368
|
},
|
|
1248
1369
|
queryType: {
|
|
1249
1370
|
type: "select",
|
|
1250
1371
|
required: true,
|
|
1251
|
-
options:
|
|
1252
|
-
description: "
|
|
1372
|
+
options: QUERY_TYPES,
|
|
1373
|
+
description: "Always 'select': the Query step reads data. Use a Transform step to write."
|
|
1374
|
+
},
|
|
1375
|
+
visualConfig: {
|
|
1376
|
+
type: "object",
|
|
1377
|
+
required: false,
|
|
1378
|
+
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)."
|
|
1379
|
+
},
|
|
1380
|
+
sqlConfig: {
|
|
1381
|
+
type: "object",
|
|
1382
|
+
required: false,
|
|
1383
|
+
derived: true,
|
|
1384
|
+
description: "Compiled from visualConfig by the editor or the server. Never write it — describe the query in visualConfig."
|
|
1253
1385
|
},
|
|
1254
|
-
visualConfig: { type: "object", required: false },
|
|
1255
|
-
sqlConfig: { type: "object", required: false },
|
|
1256
1386
|
timeout: { type: "number", required: false },
|
|
1257
1387
|
maxRows: { type: "number", required: false }
|
|
1258
1388
|
};
|
|
@@ -1261,11 +1391,11 @@ var BuildQueryContractSchema = {
|
|
|
1261
1391
|
var queryDefinition = {
|
|
1262
1392
|
id: "query",
|
|
1263
1393
|
name: "Query",
|
|
1264
|
-
description: "
|
|
1265
|
-
longDescription: "
|
|
1394
|
+
description: "Read records from a connected database, app or CRM",
|
|
1395
|
+
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.",
|
|
1266
1396
|
category: "database" /* Database */,
|
|
1267
1397
|
type: "query" /* Query */,
|
|
1268
|
-
tags: ["query", "database", "
|
|
1398
|
+
tags: ["query", "database", "visual-builder", "no-code", "filter", "api", "crm", "built-in"],
|
|
1269
1399
|
status: "stable" /* Stable */,
|
|
1270
1400
|
icon: "QueryQueue",
|
|
1271
1401
|
color: "#0043ce",
|
|
@@ -1283,17 +1413,12 @@ var queryDefinition = {
|
|
|
1283
1413
|
key: "mode",
|
|
1284
1414
|
label: "Query Mode",
|
|
1285
1415
|
type: "select" /* Select */,
|
|
1286
|
-
description: "
|
|
1416
|
+
description: "Always 'visual' — the query is built with the visual builder",
|
|
1287
1417
|
options: [
|
|
1288
1418
|
{
|
|
1289
|
-
label: "Visual Builder
|
|
1419
|
+
label: "Visual Builder",
|
|
1290
1420
|
value: "visual" /* Visual */,
|
|
1291
|
-
description: "
|
|
1292
|
-
},
|
|
1293
|
-
{
|
|
1294
|
-
label: "SQL Editor (Advanced)",
|
|
1295
|
-
value: "sql" /* SQL */,
|
|
1296
|
-
description: "Write raw SQL queries with syntax highlighting"
|
|
1421
|
+
description: "Pick the table, fields and filters — no code needed"
|
|
1297
1422
|
}
|
|
1298
1423
|
],
|
|
1299
1424
|
defaultValue: "visual" /* Visual */,
|
|
@@ -1341,7 +1466,7 @@ var queryDefinition = {
|
|
|
1341
1466
|
key: "visualConfig.fields",
|
|
1342
1467
|
label: "Select Fields",
|
|
1343
1468
|
type: "array" /* Array */,
|
|
1344
|
-
description:
|
|
1469
|
+
description: 'The fields to return, as "table.column". Not needed for a summary query (aggregates / groupBy) or when every table and field is selected.',
|
|
1345
1470
|
validation: { required: true },
|
|
1346
1471
|
conditional: [
|
|
1347
1472
|
{
|
|
@@ -1384,39 +1509,6 @@ var queryDefinition = {
|
|
|
1384
1509
|
}
|
|
1385
1510
|
]
|
|
1386
1511
|
},
|
|
1387
|
-
{
|
|
1388
|
-
key: "sqlConfig.sql",
|
|
1389
|
-
label: "SQL Query",
|
|
1390
|
-
type: "code" /* Code */,
|
|
1391
|
-
language: "sql",
|
|
1392
|
-
description: "Write your SQL query here",
|
|
1393
|
-
placeholder: "SELECT * FROM users WHERE status = 'active' LIMIT 100",
|
|
1394
|
-
validation: { required: true },
|
|
1395
|
-
conditional: [
|
|
1396
|
-
{
|
|
1397
|
-
field: "mode",
|
|
1398
|
-
operator: "==",
|
|
1399
|
-
value: "sql" /* SQL */,
|
|
1400
|
-
action: "show"
|
|
1401
|
-
}
|
|
1402
|
-
]
|
|
1403
|
-
},
|
|
1404
|
-
{
|
|
1405
|
-
key: "sqlConfig.parameters",
|
|
1406
|
-
label: "Query Parameters",
|
|
1407
|
-
type: "json" /* JSON */,
|
|
1408
|
-
description: 'Parameters for parameterized queries (e.g., {"userId": 123})',
|
|
1409
|
-
validation: { required: false },
|
|
1410
|
-
advanced: true,
|
|
1411
|
-
conditional: [
|
|
1412
|
-
{
|
|
1413
|
-
field: "mode",
|
|
1414
|
-
operator: "==",
|
|
1415
|
-
value: "sql" /* SQL */,
|
|
1416
|
-
action: "show"
|
|
1417
|
-
}
|
|
1418
|
-
]
|
|
1419
|
-
},
|
|
1420
1512
|
{
|
|
1421
1513
|
key: "timeout",
|
|
1422
1514
|
label: "Query Timeout (ms)",
|
|
@@ -1430,7 +1522,7 @@ var queryDefinition = {
|
|
|
1430
1522
|
key: "maxRows",
|
|
1431
1523
|
label: "Max Rows",
|
|
1432
1524
|
type: "number" /* Number */,
|
|
1433
|
-
description: "Legacy step-level field —
|
|
1525
|
+
description: "Legacy step-level field — not read at run. To cap a run, set visualConfig.maxRows.",
|
|
1434
1526
|
defaultValue: 1000,
|
|
1435
1527
|
validation: { required: false, min: 1, max: 1e4 },
|
|
1436
1528
|
advanced: true
|
|
@@ -1446,7 +1538,7 @@ var queryDefinition = {
|
|
|
1446
1538
|
{
|
|
1447
1539
|
id: "query_mode",
|
|
1448
1540
|
label: "Query Configuration",
|
|
1449
|
-
description: "
|
|
1541
|
+
description: "The query is built with the visual builder",
|
|
1450
1542
|
fields: ["mode", "queryType"]
|
|
1451
1543
|
},
|
|
1452
1544
|
{
|
|
@@ -1455,12 +1547,6 @@ var queryDefinition = {
|
|
|
1455
1547
|
description: "Build your query visually without writing code",
|
|
1456
1548
|
fields: ["visualConfig.tables", "visualConfig.fields", "visualConfig.limit"]
|
|
1457
1549
|
},
|
|
1458
|
-
{
|
|
1459
|
-
id: "sql_editor",
|
|
1460
|
-
label: "SQL Editor",
|
|
1461
|
-
description: "Write raw SQL queries",
|
|
1462
|
-
fields: ["sqlConfig.sql", "sqlConfig.parameters"]
|
|
1463
|
-
},
|
|
1464
1550
|
{
|
|
1465
1551
|
id: "settings",
|
|
1466
1552
|
label: "Advanced Settings",
|
|
@@ -1500,7 +1586,7 @@ var queryDefinition = {
|
|
|
1500
1586
|
{
|
|
1501
1587
|
key: "generatedSQL",
|
|
1502
1588
|
type: "string" /* String */,
|
|
1503
|
-
description: "The
|
|
1589
|
+
description: "The query that ran, compiled from the visual builder",
|
|
1504
1590
|
example: "SELECT id, name, email FROM users WHERE status = 'active' LIMIT 100"
|
|
1505
1591
|
}
|
|
1506
1592
|
],
|
|
@@ -1626,6 +1712,32 @@ var aggregateDefinition = {
|
|
|
1626
1712
|
testable: false
|
|
1627
1713
|
};
|
|
1628
1714
|
|
|
1715
|
+
// src/catalog/storage/fileHelp.ts
|
|
1716
|
+
var FILE_OUTPUT_HELP = {
|
|
1717
|
+
operation: "The operation that ran",
|
|
1718
|
+
data: "File contents, for text files such as CSV, JSON or TXT",
|
|
1719
|
+
s3Key: "The file itself, for PDF, Excel, images and other non-text files. Pass it to Excel, CSV, IDP or an Upload step.",
|
|
1720
|
+
fileName: "File name",
|
|
1721
|
+
mimeType: "File type, e.g. text/csv or application/pdf",
|
|
1722
|
+
path: "Where the file was read from"
|
|
1723
|
+
};
|
|
1724
|
+
var FILE_INPUT_HELP = {
|
|
1725
|
+
uploadContent: "Leave blank to upload the file from the step before, or pick a value from an earlier step, or type the content.",
|
|
1726
|
+
formatContent: (format) => `The ${format} from an earlier step (a download or a generated file), or paste the ${format} here.`,
|
|
1727
|
+
excelContent: "The Excel file from an earlier step (a download or a generated file)."
|
|
1728
|
+
};
|
|
1729
|
+
var STORAGE_DOWNLOAD_OUTPUT_SCHEMA = {
|
|
1730
|
+
type: "object",
|
|
1731
|
+
properties: {
|
|
1732
|
+
operation: { type: "string", description: FILE_OUTPUT_HELP.operation },
|
|
1733
|
+
data: { type: "string", description: FILE_OUTPUT_HELP.data },
|
|
1734
|
+
s3Key: { type: "string", description: FILE_OUTPUT_HELP.s3Key },
|
|
1735
|
+
fileName: { type: "string", description: FILE_OUTPUT_HELP.fileName },
|
|
1736
|
+
mimeType: { type: "string", description: FILE_OUTPUT_HELP.mimeType },
|
|
1737
|
+
path: { type: "string", description: FILE_OUTPUT_HELP.path }
|
|
1738
|
+
}
|
|
1739
|
+
};
|
|
1740
|
+
|
|
1629
1741
|
// src/catalog/storage/csv-action/index.ts
|
|
1630
1742
|
var csvActionDefinition = {
|
|
1631
1743
|
id: "csv_action",
|
|
@@ -1640,7 +1752,7 @@ var csvActionDefinition = {
|
|
|
1640
1752
|
inputSchema: {
|
|
1641
1753
|
fields: [
|
|
1642
1754
|
{ 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 } },
|
|
1643
|
-
{ key: "content", label: "CSV Content", type: "string" /* String */, description: "
|
|
1755
|
+
{ key: "content", label: "CSV Content", type: "string" /* String */, description: FILE_INPUT_HELP.formatContent("CSV") },
|
|
1644
1756
|
{ key: "delimiter", label: "Delimiter", type: "select" /* Select */, options: [{ label: "Comma (,)", value: "," }, { label: "Semicolon (;)", value: ";" }, { label: "Tab", value: "\t" }, { label: "Pipe (|)", value: "|" }], defaultValue: "," },
|
|
1645
1757
|
{ key: "headerRow", label: "Header Row", type: "number" /* Number */, description: "Row number containing column headers (default: 1)", defaultValue: 1 },
|
|
1646
1758
|
{ 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" },
|
|
@@ -1680,7 +1792,7 @@ var jsonActionDefinition = {
|
|
|
1680
1792
|
fields: [
|
|
1681
1793
|
{ 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 } },
|
|
1682
1794
|
{ key: "format", label: "Format", type: "select" /* Select */, options: [{ label: "JSON (array)", value: "json" }, { label: "JSONL (one object per line)", value: "jsonl" }], defaultValue: "json" },
|
|
1683
|
-
{ key: "content", label: "JSON Content", type: "string" /* String */, description: "
|
|
1795
|
+
{ key: "content", label: "JSON Content", type: "string" /* String */, description: FILE_INPUT_HELP.formatContent("JSON") },
|
|
1684
1796
|
{ key: "prettyPrint", label: "Pretty Print", type: "boolean" /* Boolean */, description: "Format output with indentation", defaultValue: true },
|
|
1685
1797
|
{ key: "fileName", label: "Output File Name", type: "string" /* String */, defaultValue: "export.json" }
|
|
1686
1798
|
]
|
|
@@ -1716,7 +1828,7 @@ var xmlActionDefinition = {
|
|
|
1716
1828
|
inputSchema: {
|
|
1717
1829
|
fields: [
|
|
1718
1830
|
{ 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 } },
|
|
1719
|
-
{ key: "content", label: "XML Content", type: "string" /* String */, description: "
|
|
1831
|
+
{ key: "content", label: "XML Content", type: "string" /* String */, description: FILE_INPUT_HELP.formatContent("XML") },
|
|
1720
1832
|
{ key: "rootElement", label: "Root Element", type: "string" /* String */, description: "Root XML element name (for generate)", defaultValue: "data" },
|
|
1721
1833
|
{ key: "rowElement", label: "Row Element", type: "string" /* String */, description: "Element name per record (for generate)", defaultValue: "row" },
|
|
1722
1834
|
{ key: "fileName", label: "Output File Name", type: "string" /* String */, defaultValue: "export.xml" }
|
|
@@ -1753,7 +1865,7 @@ var excelActionDefinition = {
|
|
|
1753
1865
|
inputSchema: {
|
|
1754
1866
|
fields: [
|
|
1755
1867
|
{ 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 } },
|
|
1756
|
-
{ key: "content", label: "Excel File", type: "string" /* String */, description:
|
|
1868
|
+
{ key: "content", label: "Excel File", type: "string" /* String */, description: FILE_INPUT_HELP.excelContent },
|
|
1757
1869
|
{ key: "sheetName", label: "Sheet Name", type: "string" /* String */, description: "Name of sheet to read (default: first sheet)" },
|
|
1758
1870
|
{ key: "headerRow", label: "Header Row", type: "number" /* Number */, description: "Row number containing headers", defaultValue: 1 },
|
|
1759
1871
|
{ key: "fileName", label: "Output File Name", type: "string" /* String */, defaultValue: "export.xlsx" }
|
|
@@ -1765,7 +1877,7 @@ var excelActionDefinition = {
|
|
|
1765
1877
|
{ key: "data", type: "array" /* Array */, description: "Parsed records (parse mode) — loop these with a ForEach. File metadata in generate mode." },
|
|
1766
1878
|
{ key: "columns", type: "json" /* JSON */, description: "Column names" },
|
|
1767
1879
|
{ key: "rowCount", type: "number" /* Number */, description: "Number of records" },
|
|
1768
|
-
{ key: "s3Key", type: "string" /* String */, description: "
|
|
1880
|
+
{ key: "s3Key", type: "string" /* String */, description: "The generated Excel file. Pass it to an Upload step to save it." }
|
|
1769
1881
|
],
|
|
1770
1882
|
sampleOutput: { data: [{ id: 1, name: "Widget" }], columns: ["id", "name"], rowCount: 1 }
|
|
1771
1883
|
},
|
|
@@ -1795,7 +1907,7 @@ var s3ActionDefinition = {
|
|
|
1795
1907
|
{ key: "bucket", label: "Bucket Name", type: "string" /* String */, description: "S3 bucket name (defaults to the connector’s bucket)" },
|
|
1796
1908
|
{ key: "key", label: "Object Key (Path)", type: "string" /* String */, description: "S3 object key, e.g. data/reports/monthly.csv" },
|
|
1797
1909
|
{ key: "prefix", label: "Prefix (for List)", type: "string" /* String */, description: "Filter objects by prefix (folder path)" },
|
|
1798
|
-
{ key: "content", label: "File Content (for Upload)", type: "string" /* String */, description:
|
|
1910
|
+
{ key: "content", label: "File Content (for Upload)", type: "string" /* String */, description: FILE_INPUT_HELP.uploadContent },
|
|
1799
1911
|
{ key: "contentType", label: "Content Type", type: "string" /* String */, description: "MIME type (e.g. text/csv, application/json)", defaultValue: "application/octet-stream" },
|
|
1800
1912
|
{ key: "destBucket", label: "Destination Bucket (for Copy)", type: "string" /* String */ },
|
|
1801
1913
|
{ key: "destKey", label: "Destination Key (for Copy)", type: "string" /* String */ },
|
|
@@ -1836,7 +1948,7 @@ var ftpActionDefinition = {
|
|
|
1836
1948
|
{ key: "connector", label: "FTP / SFTP Connection", type: "connection" /* Connection */, description: "Select a configured FTP/SFTP connector from Integrations", validation: { required: true } },
|
|
1837
1949
|
{ 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 } },
|
|
1838
1950
|
{ key: "remotePath", label: "Remote Path", type: "string" /* String */, description: "File or folder path on the server (use file explorer to browse)" },
|
|
1839
|
-
{ key: "content", label: "File Content (Upload)", type: "string" /* String */, description:
|
|
1951
|
+
{ key: "content", label: "File Content (Upload)", type: "string" /* String */, description: FILE_INPUT_HELP.uploadContent },
|
|
1840
1952
|
{ key: "fileName", label: "File Name (Upload)", type: "string" /* String */, description: "Name for the uploaded file" },
|
|
1841
1953
|
{ key: "destPath", label: "Destination Path (Move)", type: "string" /* String */, description: "New path for moved/renamed file" },
|
|
1842
1954
|
{ key: "ifExists", label: "If File Exists", type: "select" /* Select */, options: [{ label: "Overwrite", value: "overwrite" }, { label: "Skip", value: "skip" }], defaultValue: "overwrite" }
|
|
@@ -1877,7 +1989,7 @@ var googleDriveActionDefinition = {
|
|
|
1877
1989
|
{ key: "fileId", label: "File ID / Path", type: "string" /* String */, description: "Google Drive file ID (use file explorer to browse)" },
|
|
1878
1990
|
{ key: "folderId", label: "Folder ID", type: "string" /* String */, description: "Target folder (root if empty)" },
|
|
1879
1991
|
{ key: "fileName", label: "File Name", type: "string" /* String */ },
|
|
1880
|
-
{ key: "content", label: "File Content (Upload)", type: "string" /* String */, description:
|
|
1992
|
+
{ key: "content", label: "File Content (Upload)", type: "string" /* String */, description: FILE_INPUT_HELP.uploadContent },
|
|
1881
1993
|
{ key: "mimeType", label: "MIME Type", type: "string" /* String */, defaultValue: "application/octet-stream" },
|
|
1882
1994
|
{ key: "destFolderId", label: "Destination Folder (Move/Copy)", type: "string" /* String */ },
|
|
1883
1995
|
{ key: "shareEmail", label: "Share With Email", type: "string" /* String */ },
|
|
@@ -1919,7 +2031,7 @@ var dropboxActionDefinition = {
|
|
|
1919
2031
|
{ key: "connector", label: "Dropbox Account", type: "connection" /* Connection */, description: "Select a connected Dropbox account", validation: { required: true } },
|
|
1920
2032
|
{ 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 } },
|
|
1921
2033
|
{ key: "path", label: "File / Folder Path", type: "string" /* String */, description: "Path on Dropbox (use file explorer to browse)" },
|
|
1922
|
-
{ key: "content", label: "File Content (Upload)", type: "string" /* String */, description:
|
|
2034
|
+
{ key: "content", label: "File Content (Upload)", type: "string" /* String */, description: FILE_INPUT_HELP.uploadContent },
|
|
1923
2035
|
{ key: "fileName", label: "File Name (Upload)", type: "string" /* String */ },
|
|
1924
2036
|
{ key: "destPath", label: "Destination Path (Move/Copy)", type: "string" /* String */ },
|
|
1925
2037
|
{ key: "mode", label: "Upload Mode", type: "select" /* Select */, options: [{ label: "Add (fail if exists)", value: "add" }, { label: "Overwrite", value: "overwrite" }], defaultValue: "overwrite" }
|
|
@@ -1959,7 +2071,7 @@ var onedriveActionDefinition = {
|
|
|
1959
2071
|
{ 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 } },
|
|
1960
2072
|
{ key: "path", label: "File / Folder Path", type: "string" /* String */, description: "Path on OneDrive (use file explorer to browse)" },
|
|
1961
2073
|
{ key: "itemId", label: "Item ID", type: "string" /* String */, description: "OneDrive item ID (alternative to path)" },
|
|
1962
|
-
{ key: "content", label: "File Content (Upload)", type: "string" /* String */, description:
|
|
2074
|
+
{ key: "content", label: "File Content (Upload)", type: "string" /* String */, description: FILE_INPUT_HELP.uploadContent },
|
|
1963
2075
|
{ key: "fileName", label: "File Name (Upload)", type: "string" /* String */ },
|
|
1964
2076
|
{ key: "destPath", label: "Destination Path (Move/Copy)", type: "string" /* String */ }
|
|
1965
2077
|
]
|
|
@@ -2017,14 +2129,17 @@ var storageActionDefinition = {
|
|
|
2017
2129
|
},
|
|
2018
2130
|
{ key: "path", label: "Path", type: "string" /* String */, description: "File or folder path — use the file browser in the panel." },
|
|
2019
2131
|
{ key: "destPath", label: "Destination path", type: "string" /* String */, description: "Target for copy / move." },
|
|
2020
|
-
{ key: "content", label: "File content (for Upload)", type: "string" /* String */, description:
|
|
2132
|
+
{ key: "content", label: "File content (for Upload)", type: "string" /* String */, description: FILE_INPUT_HELP.uploadContent }
|
|
2021
2133
|
]
|
|
2022
2134
|
},
|
|
2023
2135
|
outputSchema: {
|
|
2024
2136
|
type: "object",
|
|
2025
2137
|
properties: [
|
|
2026
2138
|
{ key: "operation", type: "string" /* String */, description: "The operation that ran" },
|
|
2027
|
-
{ key: "data", type: "json" /* JSON */, description:
|
|
2139
|
+
{ key: "data", type: "json" /* JSON */, description: `Download: ${FILE_OUTPUT_HELP.data}. Other operations: their result.` },
|
|
2140
|
+
{ key: "s3Key", type: "string" /* String */, description: `Download: ${FILE_OUTPUT_HELP.s3Key}` },
|
|
2141
|
+
{ key: "fileName", type: "string" /* String */, description: `Download: ${FILE_OUTPUT_HELP.fileName}` },
|
|
2142
|
+
{ key: "mimeType", type: "string" /* String */, description: `Download: ${FILE_OUTPUT_HELP.mimeType}` },
|
|
2028
2143
|
{ key: "files", type: "json" /* JSON */, description: "Listing (for the List operation)" },
|
|
2029
2144
|
{ key: "path", type: "string" /* String */, description: "Affected file/folder path" },
|
|
2030
2145
|
{ key: "url", type: "string" /* String */, description: "Signed URL (for the Get signed URL operation)" }
|
|
@@ -2666,6 +2781,10 @@ function isSatisfiedOutOfBand(actionId, fieldKey, config) {
|
|
|
2666
2781
|
return true;
|
|
2667
2782
|
return Array.isArray(config.blocks) && config.blocks.length > 0;
|
|
2668
2783
|
}
|
|
2784
|
+
if (actionId === "query" && fieldKey === "visualConfig.fields")
|
|
2785
|
+
return queryFieldsSatisfied(config);
|
|
2786
|
+
if (actionId === "query" && fieldKey === "visualConfig.tables")
|
|
2787
|
+
return queryTablesSatisfied(config);
|
|
2669
2788
|
return false;
|
|
2670
2789
|
}
|
|
2671
2790
|
var MESSAGE_BODY_ACTIONS = new Set([
|
|
@@ -3560,9 +3679,6 @@ var EmailConfig = {
|
|
|
3560
3679
|
}
|
|
3561
3680
|
}
|
|
3562
3681
|
};
|
|
3563
|
-
// src/catalog/isOneOf.ts
|
|
3564
|
-
var isOneOf = (set, value) => typeof value === "string" && set.includes(value);
|
|
3565
|
-
|
|
3566
3682
|
// src/catalog/general/transform/config.ts
|
|
3567
3683
|
var TRANSFORM_MODES = ["visual", "expression"];
|
|
3568
3684
|
var TRANSFORM_OPERATIONS = [
|
|
@@ -4470,10 +4586,10 @@ var FILE_EDIT_SCHEMA = {
|
|
|
4470
4586
|
matchOp: { type: "select", required: false, options: FILE_EDIT_MATCH_OPS, description: 'set_fields: comparison; "=" by default' },
|
|
4471
4587
|
matchValue: { type: "string", required: false, description: "set_fields: value compared against" },
|
|
4472
4588
|
setFields: { type: "object", required: false, description: "set_fields: { column: newValue } applied to matching rows" },
|
|
4473
|
-
destination: { type: "select", required: false, options: FILE_EDIT_DESTINATIONS, description: '"new" writes an edited copy (default); "in_place" overwrites
|
|
4589
|
+
destination: { type: "select", required: false, options: FILE_EDIT_DESTINATIONS, description: '"new" writes an edited copy (default); "in_place" overwrites the source file' },
|
|
4474
4590
|
outputFileName: { type: "string", required: false, description: 'Name of the edited copy when destination is "new"' },
|
|
4475
4591
|
source: { type: "select", required: false, options: FILE_EDIT_SOURCES, description: "Where the file to edit comes from; inferred from content when absent" },
|
|
4476
|
-
s3Key: { type: "string", required: false, description:
|
|
4592
|
+
s3Key: { type: "string", required: false, description: `source "s3": an earlier step's file (its s3Key output) to edit` },
|
|
4477
4593
|
url: { type: "url", required: false, description: 'source "url": the file to edit is fetched from here' }
|
|
4478
4594
|
};
|
|
4479
4595
|
function fileEditErrors(config) {
|
|
@@ -4505,7 +4621,7 @@ var CsvActionConfig = {
|
|
|
4505
4621
|
},
|
|
4506
4622
|
schema: {
|
|
4507
4623
|
operation: { type: "select", required: true, options: FILE_FORMAT_OPERATIONS, description: "parse (CSV → records), generate (records → CSV) or edit an existing file" },
|
|
4508
|
-
content: { type: "text", required: false, description: "CSV text, or
|
|
4624
|
+
content: { type: "text", required: false, description: "CSV text, or a reference to an earlier step's file content" },
|
|
4509
4625
|
delimiter: { type: "select", required: false, options: [",", ";", "\t", "|"] },
|
|
4510
4626
|
headerRow: { type: "number", required: false, min: 1, description: "1-based row that holds the column headers" },
|
|
4511
4627
|
encoding: { type: "string", required: false, description: "Text encoding (utf-8 by default)" },
|
|
@@ -4531,7 +4647,7 @@ var JsonActionConfig = {
|
|
|
4531
4647
|
schema: {
|
|
4532
4648
|
operation: { type: "select", required: true, options: FILE_FORMAT_OPERATIONS, description: "parse (JSON → records), generate (records → JSON) or edit an existing file" },
|
|
4533
4649
|
format: { type: "select", required: false, options: ["json", "jsonl"], description: "json (array) or jsonl (one object per line)" },
|
|
4534
|
-
content: { type: "text", required: false, description: "JSON text, or
|
|
4650
|
+
content: { type: "text", required: false, description: "JSON text, or a reference to an earlier step's file content" },
|
|
4535
4651
|
prettyPrint: { type: "boolean", required: false, description: "generate: indent the output" },
|
|
4536
4652
|
fileName: { type: "string", required: false, description: "generate: name of the produced file" },
|
|
4537
4653
|
jsonl: { type: "boolean", required: false, description: "edit: treat the file as JSONL" },
|
|
@@ -4558,7 +4674,7 @@ var XmlActionConfig = {
|
|
|
4558
4674
|
},
|
|
4559
4675
|
schema: {
|
|
4560
4676
|
operation: { type: "select", required: true, options: FILE_FORMAT_OPERATIONS, description: "parse (XML → records), generate (records → XML) or edit an existing file" },
|
|
4561
|
-
content: { type: "text", required: false, description: "XML text, or
|
|
4677
|
+
content: { type: "text", required: false, description: "XML text, or a reference to an earlier step's file content" },
|
|
4562
4678
|
rootElement: { type: "string", required: false, description: "generate: root element name" },
|
|
4563
4679
|
rowElement: { type: "string", required: false, description: "Element that holds one record" },
|
|
4564
4680
|
fileName: { type: "string", required: false, description: "generate: name of the produced file" },
|
|
@@ -4580,7 +4696,7 @@ var ExcelActionConfig = {
|
|
|
4580
4696
|
},
|
|
4581
4697
|
schema: {
|
|
4582
4698
|
operation: { type: "select", required: true, options: FILE_FORMAT_OPERATIONS, description: "parse (Excel → records), generate (records → Excel) or edit an existing file" },
|
|
4583
|
-
content: { type: "text", required: false, description: "
|
|
4699
|
+
content: { type: "text", required: false, description: "A reference to an earlier step's Excel file (its s3Key output), or base64 bytes" },
|
|
4584
4700
|
sheetName: { type: "string", required: false, description: "Sheet to read; the first sheet when absent" },
|
|
4585
4701
|
headerRow: { type: "number", required: false, min: 1, description: "1-based row that holds the column headers" },
|
|
4586
4702
|
fileName: { type: "string", required: false, description: "generate: name of the produced file" },
|
|
@@ -4598,7 +4714,7 @@ var FIXED_SCHEMA = {
|
|
|
4598
4714
|
connector: { type: "connection", required: true, description: "Storage connector instance; its provider decides the operations" },
|
|
4599
4715
|
operation: { type: "select", required: true, description: "Per provider: list, download, upload, delete, copy, move, presign, share, create_folder, transfer, sync" },
|
|
4600
4716
|
outputAs: { type: "string", required: false, description: "Name of this step's output in the run's data bag" },
|
|
4601
|
-
content: { type: "text", required: false, description: "upload: the
|
|
4717
|
+
content: { type: "text", required: false, description: "upload: the file content; blank uploads the file the step before produced" },
|
|
4602
4718
|
destConnector: { type: "connection", required: false, description: "transfer / sync: destination connector instance" },
|
|
4603
4719
|
destPath: { type: "string", required: false, description: "transfer / sync: destination path; also move / copy destination on path-based providers" },
|
|
4604
4720
|
sourceIsDirectory: { type: "boolean", required: false, description: "transfer: copy a folder recursively" },
|
|
@@ -5980,7 +6096,6 @@ export {
|
|
|
5980
6096
|
ActionStatus,
|
|
5981
6097
|
ActionType,
|
|
5982
6098
|
AggregateConfig,
|
|
5983
|
-
AggregateFunction,
|
|
5984
6099
|
AiAgentConfig,
|
|
5985
6100
|
CONDITION_OPERATORS,
|
|
5986
6101
|
CONFIG_CONTRACTS,
|
|
@@ -6002,6 +6117,8 @@ export {
|
|
|
6002
6117
|
FILE_EDIT_SCHEMA,
|
|
6003
6118
|
FILE_EDIT_SOURCES,
|
|
6004
6119
|
FILE_FORMAT_OPERATIONS,
|
|
6120
|
+
FILE_INPUT_HELP,
|
|
6121
|
+
FILE_OUTPUT_HELP,
|
|
6005
6122
|
FTP_OPERATIONS,
|
|
6006
6123
|
FieldType,
|
|
6007
6124
|
ForEachConfig,
|
|
@@ -6011,22 +6128,33 @@ export {
|
|
|
6011
6128
|
GroupConfig,
|
|
6012
6129
|
IDP_EXTRACT_STRATEGIES,
|
|
6013
6130
|
IdpExtractConfig,
|
|
6014
|
-
JoinType,
|
|
6015
6131
|
JsonActionConfig,
|
|
6016
|
-
LogicalOperator,
|
|
6017
6132
|
MAPPING_FUNCTIONS,
|
|
6018
6133
|
MAPPING_FUNCTION_SPECS,
|
|
6134
|
+
MAX_CONDITION_DEPTH,
|
|
6019
6135
|
MAX_STEP_CONFIG_BYTES,
|
|
6020
6136
|
ManualTriggerConfig,
|
|
6021
6137
|
NodeStatus,
|
|
6022
6138
|
ONEDRIVE_OPERATIONS,
|
|
6023
6139
|
OneDriveActionConfig,
|
|
6024
6140
|
PLATFORM_CONFIG_KEYS,
|
|
6141
|
+
QUERY_AGGREGATE_FUNCTIONS,
|
|
6142
|
+
QUERY_AUTHORED_SQL_MESSAGE,
|
|
6143
|
+
QUERY_COMPARISON_OPERATORS,
|
|
6144
|
+
QUERY_CONDITION_OPERATORS,
|
|
6145
|
+
QUERY_MODES,
|
|
6146
|
+
QUERY_RELATIVE_DATE_OPERATORS,
|
|
6147
|
+
QUERY_TYPES,
|
|
6148
|
+
QUERY_VISUAL_ONLY_MESSAGE,
|
|
6025
6149
|
QueryMode,
|
|
6026
6150
|
QueryType,
|
|
6151
|
+
RELATIVE_DATE_DIRECTIONS,
|
|
6152
|
+
RELATIVE_DATE_MAX_AMOUNT,
|
|
6153
|
+
RELATIVE_DATE_UNITS,
|
|
6027
6154
|
S3ActionConfig,
|
|
6028
6155
|
S3_OPERATIONS,
|
|
6029
6156
|
STORAGE_CONNECTOR_IDS,
|
|
6157
|
+
STORAGE_DOWNLOAD_OUTPUT_SCHEMA,
|
|
6030
6158
|
SortDirection,
|
|
6031
6159
|
StorageActionConfig,
|
|
6032
6160
|
TRANSFORM_MODES,
|
|
@@ -6066,6 +6194,7 @@ export {
|
|
|
6066
6194
|
getStorageProvider,
|
|
6067
6195
|
groupKeyName,
|
|
6068
6196
|
inferSchemaFromSample,
|
|
6197
|
+
isConditionGroup,
|
|
6069
6198
|
isEditorPrivateKey,
|
|
6070
6199
|
isEventCapableConnector,
|
|
6071
6200
|
isMappingFunction,
|
|
@@ -6073,6 +6202,8 @@ export {
|
|
|
6073
6202
|
mappingTransformFunctionName,
|
|
6074
6203
|
migrateDelayConfig,
|
|
6075
6204
|
queryDefinition,
|
|
6205
|
+
queryFieldsSatisfied,
|
|
6206
|
+
queryTablesSatisfied,
|
|
6076
6207
|
resolveRecordSchema,
|
|
6077
6208
|
selectsWithoutValues,
|
|
6078
6209
|
storageConfigKeys,
|
|
@@ -6081,11 +6212,13 @@ export {
|
|
|
6081
6212
|
transformConnectionToEdge,
|
|
6082
6213
|
transformFlowToWorkflow,
|
|
6083
6214
|
transformStepToNode,
|
|
6215
|
+
validateBuildQueryConfig,
|
|
6084
6216
|
validateConnectorTriggerEvents,
|
|
6085
6217
|
validateEdge,
|
|
6086
6218
|
validateFlowData,
|
|
6087
6219
|
validateNode,
|
|
6088
6220
|
validateStepConfig,
|
|
6089
6221
|
validateStepConfigs,
|
|
6222
|
+
validateVisualConfig,
|
|
6090
6223
|
validateWorkflow
|
|
6091
6224
|
};
|