catalyst-relay 0.2.1 → 0.2.4
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/README.md +5 -4
- package/dist/index.d.mts +53 -26
- package/dist/index.d.ts +53 -26
- package/dist/index.js +130 -279
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +129 -279
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
buildSQLQuery: () => buildSQLQuery,
|
|
33
34
|
createClient: () => createClient,
|
|
34
35
|
err: () => err,
|
|
35
36
|
ok: () => ok
|
|
@@ -125,93 +126,6 @@ function dictToAbapXml(data, root = "DATA") {
|
|
|
125
126
|
</asx:abap>`;
|
|
126
127
|
}
|
|
127
128
|
|
|
128
|
-
// src/core/utils/sql.ts
|
|
129
|
-
var SqlValidationError = class extends Error {
|
|
130
|
-
constructor(message) {
|
|
131
|
-
super(message);
|
|
132
|
-
this.name = "SqlValidationError";
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
function validateSqlInput(input, maxLength = 1e4) {
|
|
136
|
-
if (typeof input !== "string") {
|
|
137
|
-
return err(new SqlValidationError("Input must be a string"));
|
|
138
|
-
}
|
|
139
|
-
if (input.length > maxLength) {
|
|
140
|
-
return err(new SqlValidationError(`Input exceeds maximum length of ${maxLength}`));
|
|
141
|
-
}
|
|
142
|
-
const dangerousPatterns = [
|
|
143
|
-
{
|
|
144
|
-
pattern: /\b(DROP|DELETE|INSERT|UPDATE|ALTER|CREATE|TRUNCATE)\s+/i,
|
|
145
|
-
description: "DDL/DML keywords (DROP, DELETE, INSERT, etc.)"
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
pattern: /;[\s]*\w/,
|
|
149
|
-
description: "Statement termination followed by another statement"
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
pattern: /--[\s]*\w/,
|
|
153
|
-
description: "SQL comments with content"
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
pattern: /\/\*.*?\*\//,
|
|
157
|
-
description: "Block comments"
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
pattern: /\bEXEC(UTE)?\s*\(/i,
|
|
161
|
-
description: "Procedure execution"
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
pattern: /\bSP_\w+/i,
|
|
165
|
-
description: "Stored procedures"
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
pattern: /\bXP_\w+/i,
|
|
169
|
-
description: "Extended stored procedures"
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
pattern: /\bUNION\s+(ALL\s+)?SELECT/i,
|
|
173
|
-
description: "Union-based injection"
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
pattern: /@@\w+/,
|
|
177
|
-
description: "System variables"
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
pattern: /\bDECLARE\s+@/i,
|
|
181
|
-
description: "Variable declarations"
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
pattern: /\bCAST\s*\(/i,
|
|
185
|
-
description: "Type casting"
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
pattern: /\bCONVERT\s*\(/i,
|
|
189
|
-
description: "Type conversion"
|
|
190
|
-
}
|
|
191
|
-
];
|
|
192
|
-
for (const { pattern, description } of dangerousPatterns) {
|
|
193
|
-
if (pattern.test(input)) {
|
|
194
|
-
return err(new SqlValidationError(
|
|
195
|
-
`Input contains potentially dangerous SQL pattern: ${description}`
|
|
196
|
-
));
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
const specialCharMatches = input.match(/[;'"\\]/g);
|
|
200
|
-
const specialCharCount = specialCharMatches ? specialCharMatches.length : 0;
|
|
201
|
-
if (specialCharCount > 5) {
|
|
202
|
-
return err(new SqlValidationError("Input contains excessive special characters"));
|
|
203
|
-
}
|
|
204
|
-
const singleQuoteCount = (input.match(/'/g) || []).length;
|
|
205
|
-
if (singleQuoteCount % 2 !== 0) {
|
|
206
|
-
return err(new SqlValidationError("Unbalanced single quotes detected"));
|
|
207
|
-
}
|
|
208
|
-
const doubleQuoteCount = (input.match(/"/g) || []).length;
|
|
209
|
-
if (doubleQuoteCount % 2 !== 0) {
|
|
210
|
-
return err(new SqlValidationError("Unbalanced double quotes detected"));
|
|
211
|
-
}
|
|
212
|
-
return ok(true);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
129
|
// src/core/utils/csrf.ts
|
|
216
130
|
var FETCH_CSRF_TOKEN = "fetch";
|
|
217
131
|
var CSRF_TOKEN_HEADER = "x-csrf-token";
|
|
@@ -491,7 +405,7 @@ function requireConfig(extension) {
|
|
|
491
405
|
return [config, null];
|
|
492
406
|
}
|
|
493
407
|
|
|
494
|
-
// src/core/adt/read.ts
|
|
408
|
+
// src/core/adt/craud/read.ts
|
|
495
409
|
async function readObject(client, object) {
|
|
496
410
|
const [config, configErr] = requireConfig(object.extension);
|
|
497
411
|
if (configErr) return err(configErr);
|
|
@@ -515,7 +429,7 @@ async function readObject(client, object) {
|
|
|
515
429
|
return ok(result);
|
|
516
430
|
}
|
|
517
431
|
|
|
518
|
-
// src/core/adt/lock.ts
|
|
432
|
+
// src/core/adt/craud/lock.ts
|
|
519
433
|
async function lockObject(client, object) {
|
|
520
434
|
const [config, configErr] = requireConfig(object.extension);
|
|
521
435
|
if (configErr) return err(configErr);
|
|
@@ -563,7 +477,7 @@ async function unlockObject(client, object, lockHandle) {
|
|
|
563
477
|
return ok(void 0);
|
|
564
478
|
}
|
|
565
479
|
|
|
566
|
-
// src/core/adt/create.ts
|
|
480
|
+
// src/core/adt/craud/create.ts
|
|
567
481
|
async function createObject(client, object, packageName, transport, username) {
|
|
568
482
|
const [config, configErr] = requireConfig(object.extension);
|
|
569
483
|
if (configErr) return err(configErr);
|
|
@@ -600,7 +514,7 @@ async function createObject(client, object, packageName, transport, username) {
|
|
|
600
514
|
return ok(void 0);
|
|
601
515
|
}
|
|
602
516
|
|
|
603
|
-
// src/core/adt/update.ts
|
|
517
|
+
// src/core/adt/craud/update.ts
|
|
604
518
|
async function updateObject(client, object, lockHandle, transport) {
|
|
605
519
|
const [config, configErr] = requireConfig(object.extension);
|
|
606
520
|
if (configErr) return err(configErr);
|
|
@@ -628,7 +542,7 @@ async function updateObject(client, object, lockHandle, transport) {
|
|
|
628
542
|
return ok(void 0);
|
|
629
543
|
}
|
|
630
544
|
|
|
631
|
-
// src/core/adt/delete.ts
|
|
545
|
+
// src/core/adt/craud/delete.ts
|
|
632
546
|
async function deleteObject(client, object, lockHandle, transport) {
|
|
633
547
|
const [config, configErr] = requireConfig(object.extension);
|
|
634
548
|
if (configErr) return err(configErr);
|
|
@@ -653,7 +567,7 @@ async function deleteObject(client, object, lockHandle, transport) {
|
|
|
653
567
|
return ok(void 0);
|
|
654
568
|
}
|
|
655
569
|
|
|
656
|
-
// src/core/adt/activation.ts
|
|
570
|
+
// src/core/adt/craud/activation.ts
|
|
657
571
|
async function activateObjects(client, objects) {
|
|
658
572
|
if (objects.length === 0) {
|
|
659
573
|
return ok([]);
|
|
@@ -763,7 +677,7 @@ function extractActivationErrors(objects, xml, _extension) {
|
|
|
763
677
|
return ok(results);
|
|
764
678
|
}
|
|
765
679
|
|
|
766
|
-
// src/core/adt/tree.ts
|
|
680
|
+
// src/core/adt/discovery/tree.ts
|
|
767
681
|
async function getTree(client, query) {
|
|
768
682
|
const internalQuery = {};
|
|
769
683
|
if (query.package) {
|
|
@@ -880,7 +794,7 @@ function parseTreeResponse(xml) {
|
|
|
880
794
|
return ok({ nodes, packages });
|
|
881
795
|
}
|
|
882
796
|
|
|
883
|
-
// src/core/adt/packages.ts
|
|
797
|
+
// src/core/adt/discovery/packages.ts
|
|
884
798
|
async function getPackages(client) {
|
|
885
799
|
const [treeResult, treeErr] = await getTreeInternal(client, {}, "*");
|
|
886
800
|
if (treeErr) {
|
|
@@ -889,7 +803,7 @@ async function getPackages(client) {
|
|
|
889
803
|
return ok(treeResult.packages);
|
|
890
804
|
}
|
|
891
805
|
|
|
892
|
-
// src/core/adt/transports.ts
|
|
806
|
+
// src/core/adt/transports/transports.ts
|
|
893
807
|
async function getTransports(client, packageName) {
|
|
894
808
|
const contentType = "application/vnd.sap.as+xml; charset=UTF-8; dataname=com.sap.adt.transport.service.checkData";
|
|
895
809
|
const body = `<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -958,64 +872,7 @@ function extractTransports(xml) {
|
|
|
958
872
|
return ok(transports);
|
|
959
873
|
}
|
|
960
874
|
|
|
961
|
-
// src/core/adt/
|
|
962
|
-
function buildWhereClauses(filters) {
|
|
963
|
-
if (!filters || filters.length === 0) {
|
|
964
|
-
return "";
|
|
965
|
-
}
|
|
966
|
-
const clauses = filters.map((filter) => {
|
|
967
|
-
const { column, operator, value } = filter;
|
|
968
|
-
switch (operator) {
|
|
969
|
-
case "eq":
|
|
970
|
-
return `${column} = ${formatValue(value)}`;
|
|
971
|
-
case "ne":
|
|
972
|
-
return `${column} != ${formatValue(value)}`;
|
|
973
|
-
case "gt":
|
|
974
|
-
return `${column} > ${formatValue(value)}`;
|
|
975
|
-
case "ge":
|
|
976
|
-
return `${column} >= ${formatValue(value)}`;
|
|
977
|
-
case "lt":
|
|
978
|
-
return `${column} < ${formatValue(value)}`;
|
|
979
|
-
case "le":
|
|
980
|
-
return `${column} <= ${formatValue(value)}`;
|
|
981
|
-
case "like":
|
|
982
|
-
return `${column} LIKE ${formatValue(value)}`;
|
|
983
|
-
case "in":
|
|
984
|
-
if (Array.isArray(value)) {
|
|
985
|
-
const values = value.map((v) => formatValue(v)).join(", ");
|
|
986
|
-
return `${column} IN (${values})`;
|
|
987
|
-
}
|
|
988
|
-
return `${column} IN (${formatValue(value)})`;
|
|
989
|
-
default:
|
|
990
|
-
return "";
|
|
991
|
-
}
|
|
992
|
-
}).filter((c) => c);
|
|
993
|
-
if (clauses.length === 0) {
|
|
994
|
-
return "";
|
|
995
|
-
}
|
|
996
|
-
return ` WHERE ${clauses.join(" AND ")}`;
|
|
997
|
-
}
|
|
998
|
-
function buildOrderByClauses(orderBy) {
|
|
999
|
-
if (!orderBy || orderBy.length === 0) {
|
|
1000
|
-
return "";
|
|
1001
|
-
}
|
|
1002
|
-
const clauses = orderBy.map((o) => `${o.column} ${o.direction.toUpperCase()}`);
|
|
1003
|
-
return ` ORDER BY ${clauses.join(", ")}`;
|
|
1004
|
-
}
|
|
1005
|
-
function formatValue(value) {
|
|
1006
|
-
if (value === null) {
|
|
1007
|
-
return "NULL";
|
|
1008
|
-
}
|
|
1009
|
-
if (typeof value === "string") {
|
|
1010
|
-
return `'${value.replace(/'/g, "''")}'`;
|
|
1011
|
-
}
|
|
1012
|
-
if (typeof value === "boolean") {
|
|
1013
|
-
return value ? "1" : "0";
|
|
1014
|
-
}
|
|
1015
|
-
return String(value);
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
// src/core/adt/previewParser.ts
|
|
875
|
+
// src/core/adt/data_extraction/previewParser.ts
|
|
1019
876
|
function parseDataPreview(xml, maxRows, isTable) {
|
|
1020
877
|
const [doc, parseErr] = safeParseXml(xml);
|
|
1021
878
|
if (parseErr) {
|
|
@@ -1033,10 +890,18 @@ function parseDataPreview(xml, maxRows, isTable) {
|
|
|
1033
890
|
if (!name || !dataType) continue;
|
|
1034
891
|
columns.push({ name, dataType });
|
|
1035
892
|
}
|
|
893
|
+
const dataSetElements = doc.getElementsByTagNameNS(namespace, "dataSet");
|
|
894
|
+
if (columns.length === 0 && dataSetElements.length > 0) {
|
|
895
|
+
for (let i = 0; i < dataSetElements.length; i++) {
|
|
896
|
+
const dataSet = dataSetElements[i];
|
|
897
|
+
if (!dataSet) continue;
|
|
898
|
+
const name = dataSet.getAttributeNS(namespace, "columnName") || dataSet.getAttribute("columnName") || `column${i}`;
|
|
899
|
+
columns.push({ name, dataType: "unknown" });
|
|
900
|
+
}
|
|
901
|
+
}
|
|
1036
902
|
if (columns.length === 0) {
|
|
1037
|
-
return
|
|
903
|
+
return ok({ columns: [], rows: [], totalRows: 0 });
|
|
1038
904
|
}
|
|
1039
|
-
const dataSetElements = doc.getElementsByTagNameNS(namespace, "dataSet");
|
|
1040
905
|
const columnData = Array.from({ length: columns.length }, () => []);
|
|
1041
906
|
for (let i = 0; i < dataSetElements.length; i++) {
|
|
1042
907
|
const dataSet = dataSetElements[i];
|
|
@@ -1066,23 +931,16 @@ function parseDataPreview(xml, maxRows, isTable) {
|
|
|
1066
931
|
return ok(dataFrame);
|
|
1067
932
|
}
|
|
1068
933
|
|
|
1069
|
-
// src/core/adt/
|
|
934
|
+
// src/core/adt/data_extraction/dataPreview.ts
|
|
1070
935
|
async function previewData(client, query) {
|
|
1071
936
|
const extension = query.objectType === "table" ? "astabldt" : "asddls";
|
|
1072
937
|
const config = getConfigByExtension(extension);
|
|
1073
|
-
if (!config
|
|
938
|
+
if (!config?.dpEndpoint || !config?.dpParam) {
|
|
1074
939
|
return err(new Error(`Data preview not supported for object type: ${query.objectType}`));
|
|
1075
940
|
}
|
|
1076
941
|
const limit = query.limit ?? 100;
|
|
1077
|
-
const whereClauses = buildWhereClauses(query.filters);
|
|
1078
|
-
const orderByClauses = buildOrderByClauses(query.orderBy);
|
|
1079
|
-
const sqlQuery = `select * from ${query.objectName}${whereClauses}${orderByClauses}`;
|
|
1080
|
-
const [, validationErr] = validateSqlInput(sqlQuery);
|
|
1081
|
-
if (validationErr) {
|
|
1082
|
-
return err(new Error(`SQL validation failed: ${validationErr.message}`));
|
|
1083
|
-
}
|
|
1084
942
|
debug(`Data preview: endpoint=${config.dpEndpoint}, param=${config.dpParam}=${query.objectName}`);
|
|
1085
|
-
debug(`SQL: ${sqlQuery}`);
|
|
943
|
+
debug(`SQL: ${query.sqlQuery}`);
|
|
1086
944
|
const [response, requestErr] = await client.request({
|
|
1087
945
|
method: "POST",
|
|
1088
946
|
path: `/sap/bc/adt/datapreview/${config.dpEndpoint}`,
|
|
@@ -1094,7 +952,7 @@ async function previewData(client, query) {
|
|
|
1094
952
|
"Accept": "application/vnd.sap.adt.datapreview.table.v1+xml",
|
|
1095
953
|
"Content-Type": "text/plain"
|
|
1096
954
|
},
|
|
1097
|
-
body: sqlQuery
|
|
955
|
+
body: query.sqlQuery
|
|
1098
956
|
});
|
|
1099
957
|
if (requestErr) {
|
|
1100
958
|
return err(requestErr);
|
|
@@ -1113,119 +971,123 @@ async function previewData(client, query) {
|
|
|
1113
971
|
return ok(dataFrame);
|
|
1114
972
|
}
|
|
1115
973
|
|
|
1116
|
-
// src/core/adt/
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
if (
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
974
|
+
// src/core/adt/data_extraction/queryBuilder.ts
|
|
975
|
+
function quoteString(value) {
|
|
976
|
+
return typeof value == "string" ? "'" + value + "'" : "" + value;
|
|
977
|
+
}
|
|
978
|
+
function basicFilterToWhere(filter) {
|
|
979
|
+
return `${filter.field} ${filter.operator} ${quoteString(filter.value)}`;
|
|
980
|
+
}
|
|
981
|
+
function betweenFilterToWhere(filter) {
|
|
982
|
+
return `${filter.field} between ${quoteString(filter.minimum)} and ${quoteString(filter.maximum)}`;
|
|
983
|
+
}
|
|
984
|
+
function listFilterToWhere(filter) {
|
|
985
|
+
return `${filter.field} ${filter.include ? "" : "not "}in ( ${filter.values.map(quoteString).join(", ")} )`;
|
|
986
|
+
}
|
|
987
|
+
function queryFilterToWhere(filter) {
|
|
988
|
+
if (filter.type === "list") return listFilterToWhere(filter);
|
|
989
|
+
if (filter.type === "between") return betweenFilterToWhere(filter);
|
|
990
|
+
return basicFilterToWhere(filter);
|
|
991
|
+
}
|
|
992
|
+
function queryFiltersToWhere(filters) {
|
|
993
|
+
if (filters.length === 0) return "";
|
|
994
|
+
return `
|
|
995
|
+
where ${filters.map(queryFilterToWhere).join(" and ")}`;
|
|
996
|
+
}
|
|
997
|
+
function sortingsToOrderBy(sortings) {
|
|
998
|
+
if (sortings.length === 0) return "";
|
|
999
|
+
return `
|
|
1000
|
+
order by ${sortings.map((s) => `${s.field} ${s.direction}`).join(", ")}`;
|
|
1001
|
+
}
|
|
1002
|
+
function fieldsToGroupbyClause(fields) {
|
|
1003
|
+
if (fields.length === 0) return "";
|
|
1004
|
+
return `
|
|
1005
|
+
group by ${fields.join(", ")}`;
|
|
1006
|
+
}
|
|
1007
|
+
function aggregationToFieldDefinition(aggregation) {
|
|
1008
|
+
if (aggregation.function === "count") {
|
|
1009
|
+
return `count( distinct main~${aggregation.field} ) as ${aggregation.field}`;
|
|
1010
|
+
}
|
|
1011
|
+
return `${aggregation.function}( main~${aggregation.field} ) as ${aggregation.field}`;
|
|
1012
|
+
}
|
|
1013
|
+
function parametersToSQLParams(params) {
|
|
1014
|
+
if (params.length === 0) return "";
|
|
1015
|
+
return `( ${params.map((p) => `${p.name} = ${quoteString(p.value)}`).join(", ")})`;
|
|
1016
|
+
}
|
|
1017
|
+
function buildSQLQuery(query) {
|
|
1018
|
+
const [parameters, filters, sortings, aggregations] = [query.parameters ?? [], query.filters ?? [], query.sortings ?? [], query.aggregations ?? []];
|
|
1019
|
+
const groupingFields = query.fields.filter((f) => !aggregations.find((a) => a.field === f));
|
|
1020
|
+
if (sortings.filter((s) => !query.fields.includes(s.field)).length > 0) {
|
|
1021
|
+
return err(new Error("Sorting fields must be included in the selected fields."));
|
|
1022
|
+
}
|
|
1023
|
+
let selectClause = "select\n";
|
|
1024
|
+
const fieldSelections = [];
|
|
1025
|
+
for (const field of query.fields) {
|
|
1026
|
+
const aggregation = aggregations.find((a) => a.field === field);
|
|
1027
|
+
if (aggregation) {
|
|
1028
|
+
fieldSelections.push(` ${aggregationToFieldDefinition(aggregation)}`);
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
1031
|
+
fieldSelections.push(` main~${field}`);
|
|
1168
1032
|
}
|
|
1033
|
+
selectClause += fieldSelections.join(",\n") + `
|
|
1034
|
+
from ${query.objectName}${parametersToSQLParams(parameters)} as main
|
|
1035
|
+
`;
|
|
1036
|
+
const [whereClause, groupbyClause, orderbyClause] = [queryFiltersToWhere(filters), aggregations.length ? fieldsToGroupbyClause(groupingFields) : "", sortingsToOrderBy(sortings)];
|
|
1169
1037
|
const result = {
|
|
1170
|
-
|
|
1171
|
-
|
|
1038
|
+
objectName: query.objectName,
|
|
1039
|
+
objectType: query.objectType,
|
|
1040
|
+
sqlQuery: `${selectClause}${whereClause}${groupbyClause}${orderbyClause}`
|
|
1172
1041
|
};
|
|
1042
|
+
if (query.limit !== void 0) result.limit = query.limit;
|
|
1173
1043
|
return ok(result);
|
|
1174
1044
|
}
|
|
1175
1045
|
|
|
1176
|
-
// src/core/adt/
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
const
|
|
1180
|
-
|
|
1181
|
-
|
|
1046
|
+
// src/core/adt/data_extraction/distinct.ts
|
|
1047
|
+
var MAX_ROW_COUNT = 5e4;
|
|
1048
|
+
async function getDistinctValues(client, objectName, parameters, column, objectType = "view") {
|
|
1049
|
+
const columnName = column.toUpperCase();
|
|
1050
|
+
const sqlQuery = `SELECT ${columnName} AS value, COUNT(*) AS ValueCount FROM ${objectName}${parametersToSQLParams(parameters)} GROUP BY ${columnName} ORDER BY ValueCount DESCENDING`;
|
|
1051
|
+
const [dataFrame, error] = await previewData(client, {
|
|
1052
|
+
objectName,
|
|
1053
|
+
objectType,
|
|
1054
|
+
sqlQuery,
|
|
1055
|
+
limit: MAX_ROW_COUNT
|
|
1056
|
+
});
|
|
1057
|
+
if (error) {
|
|
1058
|
+
return err(new Error(`Distinct values query failed: ${error.message}`));
|
|
1182
1059
|
}
|
|
1060
|
+
const values = dataFrame.rows.map((row) => ({
|
|
1061
|
+
value: row[0],
|
|
1062
|
+
count: parseInt(String(row[1]), 10)
|
|
1063
|
+
}));
|
|
1064
|
+
return ok({ column, values });
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// src/core/adt/data_extraction/count.ts
|
|
1068
|
+
async function countRows(client, objectName, objectType) {
|
|
1183
1069
|
const sqlQuery = `SELECT COUNT(*) AS count FROM ${objectName}`;
|
|
1184
|
-
const [,
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
method: "POST",
|
|
1190
|
-
path: `/sap/bc/adt/datapreview/${config.dpEndpoint}`,
|
|
1191
|
-
params: {
|
|
1192
|
-
"rowNumber": 1,
|
|
1193
|
-
[config.dpParam]: objectName
|
|
1194
|
-
},
|
|
1195
|
-
headers: {
|
|
1196
|
-
"Accept": "application/vnd.sap.adt.datapreview.table.v1+xml"
|
|
1197
|
-
},
|
|
1198
|
-
body: sqlQuery
|
|
1070
|
+
const [dataFrame, error] = await previewData(client, {
|
|
1071
|
+
objectName,
|
|
1072
|
+
objectType,
|
|
1073
|
+
sqlQuery,
|
|
1074
|
+
limit: 1
|
|
1199
1075
|
});
|
|
1200
|
-
if (
|
|
1201
|
-
return err(
|
|
1202
|
-
}
|
|
1203
|
-
if (!response.ok) {
|
|
1204
|
-
const text2 = await response.text();
|
|
1205
|
-
const errorMsg = extractError(text2);
|
|
1206
|
-
return err(new Error(`Row count query failed: ${errorMsg}`));
|
|
1076
|
+
if (error) {
|
|
1077
|
+
return err(new Error(`Row count query failed: ${error.message}`));
|
|
1207
1078
|
}
|
|
1208
|
-
const
|
|
1209
|
-
|
|
1210
|
-
if (parseErr) {
|
|
1211
|
-
return err(parseErr);
|
|
1212
|
-
}
|
|
1213
|
-
const dataElements = doc.getElementsByTagNameNS("http://www.sap.com/adt/dataPreview", "data");
|
|
1214
|
-
if (dataElements.length === 0) {
|
|
1079
|
+
const countValue = dataFrame.rows[0]?.[0];
|
|
1080
|
+
if (countValue === void 0) {
|
|
1215
1081
|
return err(new Error("No count value returned"));
|
|
1216
1082
|
}
|
|
1217
|
-
const
|
|
1218
|
-
if (!countText) {
|
|
1219
|
-
return err(new Error("Empty count value returned"));
|
|
1220
|
-
}
|
|
1221
|
-
const count = parseInt(countText, 10);
|
|
1083
|
+
const count = parseInt(String(countValue), 10);
|
|
1222
1084
|
if (isNaN(count)) {
|
|
1223
1085
|
return err(new Error("Invalid count value returned"));
|
|
1224
1086
|
}
|
|
1225
1087
|
return ok(count);
|
|
1226
1088
|
}
|
|
1227
1089
|
|
|
1228
|
-
// src/core/adt/searchObjects.ts
|
|
1090
|
+
// src/core/adt/discovery/searchObjects.ts
|
|
1229
1091
|
async function searchObjects(client, query, types) {
|
|
1230
1092
|
const searchPattern = query || "*";
|
|
1231
1093
|
const objectTypes = types && types.length > 0 ? types : getAllTypes();
|
|
@@ -1292,7 +1154,7 @@ function parseSearchResults(xml) {
|
|
|
1292
1154
|
return ok(results);
|
|
1293
1155
|
}
|
|
1294
1156
|
|
|
1295
|
-
// src/core/adt/whereUsed.ts
|
|
1157
|
+
// src/core/adt/discovery/whereUsed.ts
|
|
1296
1158
|
async function findWhereUsed(client, object) {
|
|
1297
1159
|
const config = getConfigByExtension(object.extension);
|
|
1298
1160
|
if (!config) {
|
|
@@ -1366,7 +1228,7 @@ function parseWhereUsed(xml) {
|
|
|
1366
1228
|
return ok(dependencies);
|
|
1367
1229
|
}
|
|
1368
1230
|
|
|
1369
|
-
// src/core/adt/createTransport.ts
|
|
1231
|
+
// src/core/adt/transports/createTransport.ts
|
|
1370
1232
|
async function createTransport(client, config) {
|
|
1371
1233
|
const body = dictToAbapXml({
|
|
1372
1234
|
DEVCLASS: config.package,
|
|
@@ -1397,7 +1259,7 @@ async function createTransport(client, config) {
|
|
|
1397
1259
|
return ok(transportId);
|
|
1398
1260
|
}
|
|
1399
1261
|
|
|
1400
|
-
// src/core/adt/gitDiff.ts
|
|
1262
|
+
// src/core/adt/craud/gitDiff.ts
|
|
1401
1263
|
var import_diff = require("diff");
|
|
1402
1264
|
function computeDiff(serverLines, localLines) {
|
|
1403
1265
|
const changes = (0, import_diff.diffArrays)(serverLines, localLines);
|
|
@@ -2345,9 +2207,9 @@ var ADTClientImpl = class {
|
|
|
2345
2207
|
if (!this.state.session) return err(new Error("Not logged in"));
|
|
2346
2208
|
return previewData(this.requestor, query);
|
|
2347
2209
|
}
|
|
2348
|
-
async getDistinctValues(objectName, column, objectType = "view") {
|
|
2210
|
+
async getDistinctValues(objectName, parameters, column, objectType = "view") {
|
|
2349
2211
|
if (!this.state.session) return err(new Error("Not logged in"));
|
|
2350
|
-
return getDistinctValues(this.requestor, objectName, column, objectType);
|
|
2212
|
+
return getDistinctValues(this.requestor, objectName, parameters, column, objectType);
|
|
2351
2213
|
}
|
|
2352
2214
|
async countRows(objectName, objectType) {
|
|
2353
2215
|
if (!this.state.session) return err(new Error("Not logged in"));
|
|
@@ -2392,20 +2254,9 @@ function createClient(config) {
|
|
|
2392
2254
|
}
|
|
2393
2255
|
return ok(new ADTClientImpl(config));
|
|
2394
2256
|
}
|
|
2395
|
-
|
|
2396
|
-
// src/core/config.ts
|
|
2397
|
-
var import_node_fs = require("fs");
|
|
2398
|
-
var import_zod2 = require("zod");
|
|
2399
|
-
var systemConfigSchema = import_zod2.z.record(
|
|
2400
|
-
import_zod2.z.string(),
|
|
2401
|
-
import_zod2.z.object({
|
|
2402
|
-
adt: import_zod2.z.string().url().optional(),
|
|
2403
|
-
odata: import_zod2.z.string().url().optional(),
|
|
2404
|
-
instance_num: import_zod2.z.string().optional()
|
|
2405
|
-
})
|
|
2406
|
-
);
|
|
2407
2257
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2408
2258
|
0 && (module.exports = {
|
|
2259
|
+
buildSQLQuery,
|
|
2409
2260
|
createClient,
|
|
2410
2261
|
err,
|
|
2411
2262
|
ok
|