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.mjs
CHANGED
|
@@ -94,93 +94,6 @@ function dictToAbapXml(data, root = "DATA") {
|
|
|
94
94
|
</asx:abap>`;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
// src/core/utils/sql.ts
|
|
98
|
-
var SqlValidationError = class extends Error {
|
|
99
|
-
constructor(message) {
|
|
100
|
-
super(message);
|
|
101
|
-
this.name = "SqlValidationError";
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
function validateSqlInput(input, maxLength = 1e4) {
|
|
105
|
-
if (typeof input !== "string") {
|
|
106
|
-
return err(new SqlValidationError("Input must be a string"));
|
|
107
|
-
}
|
|
108
|
-
if (input.length > maxLength) {
|
|
109
|
-
return err(new SqlValidationError(`Input exceeds maximum length of ${maxLength}`));
|
|
110
|
-
}
|
|
111
|
-
const dangerousPatterns = [
|
|
112
|
-
{
|
|
113
|
-
pattern: /\b(DROP|DELETE|INSERT|UPDATE|ALTER|CREATE|TRUNCATE)\s+/i,
|
|
114
|
-
description: "DDL/DML keywords (DROP, DELETE, INSERT, etc.)"
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
pattern: /;[\s]*\w/,
|
|
118
|
-
description: "Statement termination followed by another statement"
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
pattern: /--[\s]*\w/,
|
|
122
|
-
description: "SQL comments with content"
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
pattern: /\/\*.*?\*\//,
|
|
126
|
-
description: "Block comments"
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
pattern: /\bEXEC(UTE)?\s*\(/i,
|
|
130
|
-
description: "Procedure execution"
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
pattern: /\bSP_\w+/i,
|
|
134
|
-
description: "Stored procedures"
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
pattern: /\bXP_\w+/i,
|
|
138
|
-
description: "Extended stored procedures"
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
pattern: /\bUNION\s+(ALL\s+)?SELECT/i,
|
|
142
|
-
description: "Union-based injection"
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
pattern: /@@\w+/,
|
|
146
|
-
description: "System variables"
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
pattern: /\bDECLARE\s+@/i,
|
|
150
|
-
description: "Variable declarations"
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
pattern: /\bCAST\s*\(/i,
|
|
154
|
-
description: "Type casting"
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
pattern: /\bCONVERT\s*\(/i,
|
|
158
|
-
description: "Type conversion"
|
|
159
|
-
}
|
|
160
|
-
];
|
|
161
|
-
for (const { pattern, description } of dangerousPatterns) {
|
|
162
|
-
if (pattern.test(input)) {
|
|
163
|
-
return err(new SqlValidationError(
|
|
164
|
-
`Input contains potentially dangerous SQL pattern: ${description}`
|
|
165
|
-
));
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
const specialCharMatches = input.match(/[;'"\\]/g);
|
|
169
|
-
const specialCharCount = specialCharMatches ? specialCharMatches.length : 0;
|
|
170
|
-
if (specialCharCount > 5) {
|
|
171
|
-
return err(new SqlValidationError("Input contains excessive special characters"));
|
|
172
|
-
}
|
|
173
|
-
const singleQuoteCount = (input.match(/'/g) || []).length;
|
|
174
|
-
if (singleQuoteCount % 2 !== 0) {
|
|
175
|
-
return err(new SqlValidationError("Unbalanced single quotes detected"));
|
|
176
|
-
}
|
|
177
|
-
const doubleQuoteCount = (input.match(/"/g) || []).length;
|
|
178
|
-
if (doubleQuoteCount % 2 !== 0) {
|
|
179
|
-
return err(new SqlValidationError("Unbalanced double quotes detected"));
|
|
180
|
-
}
|
|
181
|
-
return ok(true);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
97
|
// src/core/utils/csrf.ts
|
|
185
98
|
var FETCH_CSRF_TOKEN = "fetch";
|
|
186
99
|
var CSRF_TOKEN_HEADER = "x-csrf-token";
|
|
@@ -460,7 +373,7 @@ function requireConfig(extension) {
|
|
|
460
373
|
return [config, null];
|
|
461
374
|
}
|
|
462
375
|
|
|
463
|
-
// src/core/adt/read.ts
|
|
376
|
+
// src/core/adt/craud/read.ts
|
|
464
377
|
async function readObject(client, object) {
|
|
465
378
|
const [config, configErr] = requireConfig(object.extension);
|
|
466
379
|
if (configErr) return err(configErr);
|
|
@@ -484,7 +397,7 @@ async function readObject(client, object) {
|
|
|
484
397
|
return ok(result);
|
|
485
398
|
}
|
|
486
399
|
|
|
487
|
-
// src/core/adt/lock.ts
|
|
400
|
+
// src/core/adt/craud/lock.ts
|
|
488
401
|
async function lockObject(client, object) {
|
|
489
402
|
const [config, configErr] = requireConfig(object.extension);
|
|
490
403
|
if (configErr) return err(configErr);
|
|
@@ -532,7 +445,7 @@ async function unlockObject(client, object, lockHandle) {
|
|
|
532
445
|
return ok(void 0);
|
|
533
446
|
}
|
|
534
447
|
|
|
535
|
-
// src/core/adt/create.ts
|
|
448
|
+
// src/core/adt/craud/create.ts
|
|
536
449
|
async function createObject(client, object, packageName, transport, username) {
|
|
537
450
|
const [config, configErr] = requireConfig(object.extension);
|
|
538
451
|
if (configErr) return err(configErr);
|
|
@@ -569,7 +482,7 @@ async function createObject(client, object, packageName, transport, username) {
|
|
|
569
482
|
return ok(void 0);
|
|
570
483
|
}
|
|
571
484
|
|
|
572
|
-
// src/core/adt/update.ts
|
|
485
|
+
// src/core/adt/craud/update.ts
|
|
573
486
|
async function updateObject(client, object, lockHandle, transport) {
|
|
574
487
|
const [config, configErr] = requireConfig(object.extension);
|
|
575
488
|
if (configErr) return err(configErr);
|
|
@@ -597,7 +510,7 @@ async function updateObject(client, object, lockHandle, transport) {
|
|
|
597
510
|
return ok(void 0);
|
|
598
511
|
}
|
|
599
512
|
|
|
600
|
-
// src/core/adt/delete.ts
|
|
513
|
+
// src/core/adt/craud/delete.ts
|
|
601
514
|
async function deleteObject(client, object, lockHandle, transport) {
|
|
602
515
|
const [config, configErr] = requireConfig(object.extension);
|
|
603
516
|
if (configErr) return err(configErr);
|
|
@@ -622,7 +535,7 @@ async function deleteObject(client, object, lockHandle, transport) {
|
|
|
622
535
|
return ok(void 0);
|
|
623
536
|
}
|
|
624
537
|
|
|
625
|
-
// src/core/adt/activation.ts
|
|
538
|
+
// src/core/adt/craud/activation.ts
|
|
626
539
|
async function activateObjects(client, objects) {
|
|
627
540
|
if (objects.length === 0) {
|
|
628
541
|
return ok([]);
|
|
@@ -732,7 +645,7 @@ function extractActivationErrors(objects, xml, _extension) {
|
|
|
732
645
|
return ok(results);
|
|
733
646
|
}
|
|
734
647
|
|
|
735
|
-
// src/core/adt/tree.ts
|
|
648
|
+
// src/core/adt/discovery/tree.ts
|
|
736
649
|
async function getTree(client, query) {
|
|
737
650
|
const internalQuery = {};
|
|
738
651
|
if (query.package) {
|
|
@@ -849,7 +762,7 @@ function parseTreeResponse(xml) {
|
|
|
849
762
|
return ok({ nodes, packages });
|
|
850
763
|
}
|
|
851
764
|
|
|
852
|
-
// src/core/adt/packages.ts
|
|
765
|
+
// src/core/adt/discovery/packages.ts
|
|
853
766
|
async function getPackages(client) {
|
|
854
767
|
const [treeResult, treeErr] = await getTreeInternal(client, {}, "*");
|
|
855
768
|
if (treeErr) {
|
|
@@ -858,7 +771,7 @@ async function getPackages(client) {
|
|
|
858
771
|
return ok(treeResult.packages);
|
|
859
772
|
}
|
|
860
773
|
|
|
861
|
-
// src/core/adt/transports.ts
|
|
774
|
+
// src/core/adt/transports/transports.ts
|
|
862
775
|
async function getTransports(client, packageName) {
|
|
863
776
|
const contentType = "application/vnd.sap.as+xml; charset=UTF-8; dataname=com.sap.adt.transport.service.checkData";
|
|
864
777
|
const body = `<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -927,64 +840,7 @@ function extractTransports(xml) {
|
|
|
927
840
|
return ok(transports);
|
|
928
841
|
}
|
|
929
842
|
|
|
930
|
-
// src/core/adt/
|
|
931
|
-
function buildWhereClauses(filters) {
|
|
932
|
-
if (!filters || filters.length === 0) {
|
|
933
|
-
return "";
|
|
934
|
-
}
|
|
935
|
-
const clauses = filters.map((filter) => {
|
|
936
|
-
const { column, operator, value } = filter;
|
|
937
|
-
switch (operator) {
|
|
938
|
-
case "eq":
|
|
939
|
-
return `${column} = ${formatValue(value)}`;
|
|
940
|
-
case "ne":
|
|
941
|
-
return `${column} != ${formatValue(value)}`;
|
|
942
|
-
case "gt":
|
|
943
|
-
return `${column} > ${formatValue(value)}`;
|
|
944
|
-
case "ge":
|
|
945
|
-
return `${column} >= ${formatValue(value)}`;
|
|
946
|
-
case "lt":
|
|
947
|
-
return `${column} < ${formatValue(value)}`;
|
|
948
|
-
case "le":
|
|
949
|
-
return `${column} <= ${formatValue(value)}`;
|
|
950
|
-
case "like":
|
|
951
|
-
return `${column} LIKE ${formatValue(value)}`;
|
|
952
|
-
case "in":
|
|
953
|
-
if (Array.isArray(value)) {
|
|
954
|
-
const values = value.map((v) => formatValue(v)).join(", ");
|
|
955
|
-
return `${column} IN (${values})`;
|
|
956
|
-
}
|
|
957
|
-
return `${column} IN (${formatValue(value)})`;
|
|
958
|
-
default:
|
|
959
|
-
return "";
|
|
960
|
-
}
|
|
961
|
-
}).filter((c) => c);
|
|
962
|
-
if (clauses.length === 0) {
|
|
963
|
-
return "";
|
|
964
|
-
}
|
|
965
|
-
return ` WHERE ${clauses.join(" AND ")}`;
|
|
966
|
-
}
|
|
967
|
-
function buildOrderByClauses(orderBy) {
|
|
968
|
-
if (!orderBy || orderBy.length === 0) {
|
|
969
|
-
return "";
|
|
970
|
-
}
|
|
971
|
-
const clauses = orderBy.map((o) => `${o.column} ${o.direction.toUpperCase()}`);
|
|
972
|
-
return ` ORDER BY ${clauses.join(", ")}`;
|
|
973
|
-
}
|
|
974
|
-
function formatValue(value) {
|
|
975
|
-
if (value === null) {
|
|
976
|
-
return "NULL";
|
|
977
|
-
}
|
|
978
|
-
if (typeof value === "string") {
|
|
979
|
-
return `'${value.replace(/'/g, "''")}'`;
|
|
980
|
-
}
|
|
981
|
-
if (typeof value === "boolean") {
|
|
982
|
-
return value ? "1" : "0";
|
|
983
|
-
}
|
|
984
|
-
return String(value);
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
// src/core/adt/previewParser.ts
|
|
843
|
+
// src/core/adt/data_extraction/previewParser.ts
|
|
988
844
|
function parseDataPreview(xml, maxRows, isTable) {
|
|
989
845
|
const [doc, parseErr] = safeParseXml(xml);
|
|
990
846
|
if (parseErr) {
|
|
@@ -1002,10 +858,18 @@ function parseDataPreview(xml, maxRows, isTable) {
|
|
|
1002
858
|
if (!name || !dataType) continue;
|
|
1003
859
|
columns.push({ name, dataType });
|
|
1004
860
|
}
|
|
861
|
+
const dataSetElements = doc.getElementsByTagNameNS(namespace, "dataSet");
|
|
862
|
+
if (columns.length === 0 && dataSetElements.length > 0) {
|
|
863
|
+
for (let i = 0; i < dataSetElements.length; i++) {
|
|
864
|
+
const dataSet = dataSetElements[i];
|
|
865
|
+
if (!dataSet) continue;
|
|
866
|
+
const name = dataSet.getAttributeNS(namespace, "columnName") || dataSet.getAttribute("columnName") || `column${i}`;
|
|
867
|
+
columns.push({ name, dataType: "unknown" });
|
|
868
|
+
}
|
|
869
|
+
}
|
|
1005
870
|
if (columns.length === 0) {
|
|
1006
|
-
return
|
|
871
|
+
return ok({ columns: [], rows: [], totalRows: 0 });
|
|
1007
872
|
}
|
|
1008
|
-
const dataSetElements = doc.getElementsByTagNameNS(namespace, "dataSet");
|
|
1009
873
|
const columnData = Array.from({ length: columns.length }, () => []);
|
|
1010
874
|
for (let i = 0; i < dataSetElements.length; i++) {
|
|
1011
875
|
const dataSet = dataSetElements[i];
|
|
@@ -1035,23 +899,16 @@ function parseDataPreview(xml, maxRows, isTable) {
|
|
|
1035
899
|
return ok(dataFrame);
|
|
1036
900
|
}
|
|
1037
901
|
|
|
1038
|
-
// src/core/adt/
|
|
902
|
+
// src/core/adt/data_extraction/dataPreview.ts
|
|
1039
903
|
async function previewData(client, query) {
|
|
1040
904
|
const extension = query.objectType === "table" ? "astabldt" : "asddls";
|
|
1041
905
|
const config = getConfigByExtension(extension);
|
|
1042
|
-
if (!config
|
|
906
|
+
if (!config?.dpEndpoint || !config?.dpParam) {
|
|
1043
907
|
return err(new Error(`Data preview not supported for object type: ${query.objectType}`));
|
|
1044
908
|
}
|
|
1045
909
|
const limit = query.limit ?? 100;
|
|
1046
|
-
const whereClauses = buildWhereClauses(query.filters);
|
|
1047
|
-
const orderByClauses = buildOrderByClauses(query.orderBy);
|
|
1048
|
-
const sqlQuery = `select * from ${query.objectName}${whereClauses}${orderByClauses}`;
|
|
1049
|
-
const [, validationErr] = validateSqlInput(sqlQuery);
|
|
1050
|
-
if (validationErr) {
|
|
1051
|
-
return err(new Error(`SQL validation failed: ${validationErr.message}`));
|
|
1052
|
-
}
|
|
1053
910
|
debug(`Data preview: endpoint=${config.dpEndpoint}, param=${config.dpParam}=${query.objectName}`);
|
|
1054
|
-
debug(`SQL: ${sqlQuery}`);
|
|
911
|
+
debug(`SQL: ${query.sqlQuery}`);
|
|
1055
912
|
const [response, requestErr] = await client.request({
|
|
1056
913
|
method: "POST",
|
|
1057
914
|
path: `/sap/bc/adt/datapreview/${config.dpEndpoint}`,
|
|
@@ -1063,7 +920,7 @@ async function previewData(client, query) {
|
|
|
1063
920
|
"Accept": "application/vnd.sap.adt.datapreview.table.v1+xml",
|
|
1064
921
|
"Content-Type": "text/plain"
|
|
1065
922
|
},
|
|
1066
|
-
body: sqlQuery
|
|
923
|
+
body: query.sqlQuery
|
|
1067
924
|
});
|
|
1068
925
|
if (requestErr) {
|
|
1069
926
|
return err(requestErr);
|
|
@@ -1082,119 +939,123 @@ async function previewData(client, query) {
|
|
|
1082
939
|
return ok(dataFrame);
|
|
1083
940
|
}
|
|
1084
941
|
|
|
1085
|
-
// src/core/adt/
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
if (
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
942
|
+
// src/core/adt/data_extraction/queryBuilder.ts
|
|
943
|
+
function quoteString(value) {
|
|
944
|
+
return typeof value == "string" ? "'" + value + "'" : "" + value;
|
|
945
|
+
}
|
|
946
|
+
function basicFilterToWhere(filter) {
|
|
947
|
+
return `${filter.field} ${filter.operator} ${quoteString(filter.value)}`;
|
|
948
|
+
}
|
|
949
|
+
function betweenFilterToWhere(filter) {
|
|
950
|
+
return `${filter.field} between ${quoteString(filter.minimum)} and ${quoteString(filter.maximum)}`;
|
|
951
|
+
}
|
|
952
|
+
function listFilterToWhere(filter) {
|
|
953
|
+
return `${filter.field} ${filter.include ? "" : "not "}in ( ${filter.values.map(quoteString).join(", ")} )`;
|
|
954
|
+
}
|
|
955
|
+
function queryFilterToWhere(filter) {
|
|
956
|
+
if (filter.type === "list") return listFilterToWhere(filter);
|
|
957
|
+
if (filter.type === "between") return betweenFilterToWhere(filter);
|
|
958
|
+
return basicFilterToWhere(filter);
|
|
959
|
+
}
|
|
960
|
+
function queryFiltersToWhere(filters) {
|
|
961
|
+
if (filters.length === 0) return "";
|
|
962
|
+
return `
|
|
963
|
+
where ${filters.map(queryFilterToWhere).join(" and ")}`;
|
|
964
|
+
}
|
|
965
|
+
function sortingsToOrderBy(sortings) {
|
|
966
|
+
if (sortings.length === 0) return "";
|
|
967
|
+
return `
|
|
968
|
+
order by ${sortings.map((s) => `${s.field} ${s.direction}`).join(", ")}`;
|
|
969
|
+
}
|
|
970
|
+
function fieldsToGroupbyClause(fields) {
|
|
971
|
+
if (fields.length === 0) return "";
|
|
972
|
+
return `
|
|
973
|
+
group by ${fields.join(", ")}`;
|
|
974
|
+
}
|
|
975
|
+
function aggregationToFieldDefinition(aggregation) {
|
|
976
|
+
if (aggregation.function === "count") {
|
|
977
|
+
return `count( distinct main~${aggregation.field} ) as ${aggregation.field}`;
|
|
978
|
+
}
|
|
979
|
+
return `${aggregation.function}( main~${aggregation.field} ) as ${aggregation.field}`;
|
|
980
|
+
}
|
|
981
|
+
function parametersToSQLParams(params) {
|
|
982
|
+
if (params.length === 0) return "";
|
|
983
|
+
return `( ${params.map((p) => `${p.name} = ${quoteString(p.value)}`).join(", ")})`;
|
|
984
|
+
}
|
|
985
|
+
function buildSQLQuery(query) {
|
|
986
|
+
const [parameters, filters, sortings, aggregations] = [query.parameters ?? [], query.filters ?? [], query.sortings ?? [], query.aggregations ?? []];
|
|
987
|
+
const groupingFields = query.fields.filter((f) => !aggregations.find((a) => a.field === f));
|
|
988
|
+
if (sortings.filter((s) => !query.fields.includes(s.field)).length > 0) {
|
|
989
|
+
return err(new Error("Sorting fields must be included in the selected fields."));
|
|
990
|
+
}
|
|
991
|
+
let selectClause = "select\n";
|
|
992
|
+
const fieldSelections = [];
|
|
993
|
+
for (const field of query.fields) {
|
|
994
|
+
const aggregation = aggregations.find((a) => a.field === field);
|
|
995
|
+
if (aggregation) {
|
|
996
|
+
fieldSelections.push(` ${aggregationToFieldDefinition(aggregation)}`);
|
|
997
|
+
continue;
|
|
998
|
+
}
|
|
999
|
+
fieldSelections.push(` main~${field}`);
|
|
1137
1000
|
}
|
|
1001
|
+
selectClause += fieldSelections.join(",\n") + `
|
|
1002
|
+
from ${query.objectName}${parametersToSQLParams(parameters)} as main
|
|
1003
|
+
`;
|
|
1004
|
+
const [whereClause, groupbyClause, orderbyClause] = [queryFiltersToWhere(filters), aggregations.length ? fieldsToGroupbyClause(groupingFields) : "", sortingsToOrderBy(sortings)];
|
|
1138
1005
|
const result = {
|
|
1139
|
-
|
|
1140
|
-
|
|
1006
|
+
objectName: query.objectName,
|
|
1007
|
+
objectType: query.objectType,
|
|
1008
|
+
sqlQuery: `${selectClause}${whereClause}${groupbyClause}${orderbyClause}`
|
|
1141
1009
|
};
|
|
1010
|
+
if (query.limit !== void 0) result.limit = query.limit;
|
|
1142
1011
|
return ok(result);
|
|
1143
1012
|
}
|
|
1144
1013
|
|
|
1145
|
-
// src/core/adt/
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
const
|
|
1149
|
-
|
|
1150
|
-
|
|
1014
|
+
// src/core/adt/data_extraction/distinct.ts
|
|
1015
|
+
var MAX_ROW_COUNT = 5e4;
|
|
1016
|
+
async function getDistinctValues(client, objectName, parameters, column, objectType = "view") {
|
|
1017
|
+
const columnName = column.toUpperCase();
|
|
1018
|
+
const sqlQuery = `SELECT ${columnName} AS value, COUNT(*) AS ValueCount FROM ${objectName}${parametersToSQLParams(parameters)} GROUP BY ${columnName} ORDER BY ValueCount DESCENDING`;
|
|
1019
|
+
const [dataFrame, error] = await previewData(client, {
|
|
1020
|
+
objectName,
|
|
1021
|
+
objectType,
|
|
1022
|
+
sqlQuery,
|
|
1023
|
+
limit: MAX_ROW_COUNT
|
|
1024
|
+
});
|
|
1025
|
+
if (error) {
|
|
1026
|
+
return err(new Error(`Distinct values query failed: ${error.message}`));
|
|
1151
1027
|
}
|
|
1028
|
+
const values = dataFrame.rows.map((row) => ({
|
|
1029
|
+
value: row[0],
|
|
1030
|
+
count: parseInt(String(row[1]), 10)
|
|
1031
|
+
}));
|
|
1032
|
+
return ok({ column, values });
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
// src/core/adt/data_extraction/count.ts
|
|
1036
|
+
async function countRows(client, objectName, objectType) {
|
|
1152
1037
|
const sqlQuery = `SELECT COUNT(*) AS count FROM ${objectName}`;
|
|
1153
|
-
const [,
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
method: "POST",
|
|
1159
|
-
path: `/sap/bc/adt/datapreview/${config.dpEndpoint}`,
|
|
1160
|
-
params: {
|
|
1161
|
-
"rowNumber": 1,
|
|
1162
|
-
[config.dpParam]: objectName
|
|
1163
|
-
},
|
|
1164
|
-
headers: {
|
|
1165
|
-
"Accept": "application/vnd.sap.adt.datapreview.table.v1+xml"
|
|
1166
|
-
},
|
|
1167
|
-
body: sqlQuery
|
|
1038
|
+
const [dataFrame, error] = await previewData(client, {
|
|
1039
|
+
objectName,
|
|
1040
|
+
objectType,
|
|
1041
|
+
sqlQuery,
|
|
1042
|
+
limit: 1
|
|
1168
1043
|
});
|
|
1169
|
-
if (
|
|
1170
|
-
return err(
|
|
1171
|
-
}
|
|
1172
|
-
if (!response.ok) {
|
|
1173
|
-
const text2 = await response.text();
|
|
1174
|
-
const errorMsg = extractError(text2);
|
|
1175
|
-
return err(new Error(`Row count query failed: ${errorMsg}`));
|
|
1044
|
+
if (error) {
|
|
1045
|
+
return err(new Error(`Row count query failed: ${error.message}`));
|
|
1176
1046
|
}
|
|
1177
|
-
const
|
|
1178
|
-
|
|
1179
|
-
if (parseErr) {
|
|
1180
|
-
return err(parseErr);
|
|
1181
|
-
}
|
|
1182
|
-
const dataElements = doc.getElementsByTagNameNS("http://www.sap.com/adt/dataPreview", "data");
|
|
1183
|
-
if (dataElements.length === 0) {
|
|
1047
|
+
const countValue = dataFrame.rows[0]?.[0];
|
|
1048
|
+
if (countValue === void 0) {
|
|
1184
1049
|
return err(new Error("No count value returned"));
|
|
1185
1050
|
}
|
|
1186
|
-
const
|
|
1187
|
-
if (!countText) {
|
|
1188
|
-
return err(new Error("Empty count value returned"));
|
|
1189
|
-
}
|
|
1190
|
-
const count = parseInt(countText, 10);
|
|
1051
|
+
const count = parseInt(String(countValue), 10);
|
|
1191
1052
|
if (isNaN(count)) {
|
|
1192
1053
|
return err(new Error("Invalid count value returned"));
|
|
1193
1054
|
}
|
|
1194
1055
|
return ok(count);
|
|
1195
1056
|
}
|
|
1196
1057
|
|
|
1197
|
-
// src/core/adt/searchObjects.ts
|
|
1058
|
+
// src/core/adt/discovery/searchObjects.ts
|
|
1198
1059
|
async function searchObjects(client, query, types) {
|
|
1199
1060
|
const searchPattern = query || "*";
|
|
1200
1061
|
const objectTypes = types && types.length > 0 ? types : getAllTypes();
|
|
@@ -1261,7 +1122,7 @@ function parseSearchResults(xml) {
|
|
|
1261
1122
|
return ok(results);
|
|
1262
1123
|
}
|
|
1263
1124
|
|
|
1264
|
-
// src/core/adt/whereUsed.ts
|
|
1125
|
+
// src/core/adt/discovery/whereUsed.ts
|
|
1265
1126
|
async function findWhereUsed(client, object) {
|
|
1266
1127
|
const config = getConfigByExtension(object.extension);
|
|
1267
1128
|
if (!config) {
|
|
@@ -1335,7 +1196,7 @@ function parseWhereUsed(xml) {
|
|
|
1335
1196
|
return ok(dependencies);
|
|
1336
1197
|
}
|
|
1337
1198
|
|
|
1338
|
-
// src/core/adt/createTransport.ts
|
|
1199
|
+
// src/core/adt/transports/createTransport.ts
|
|
1339
1200
|
async function createTransport(client, config) {
|
|
1340
1201
|
const body = dictToAbapXml({
|
|
1341
1202
|
DEVCLASS: config.package,
|
|
@@ -1366,7 +1227,7 @@ async function createTransport(client, config) {
|
|
|
1366
1227
|
return ok(transportId);
|
|
1367
1228
|
}
|
|
1368
1229
|
|
|
1369
|
-
// src/core/adt/gitDiff.ts
|
|
1230
|
+
// src/core/adt/craud/gitDiff.ts
|
|
1370
1231
|
import { diffArrays } from "diff";
|
|
1371
1232
|
function computeDiff(serverLines, localLines) {
|
|
1372
1233
|
const changes = diffArrays(serverLines, localLines);
|
|
@@ -2314,9 +2175,9 @@ var ADTClientImpl = class {
|
|
|
2314
2175
|
if (!this.state.session) return err(new Error("Not logged in"));
|
|
2315
2176
|
return previewData(this.requestor, query);
|
|
2316
2177
|
}
|
|
2317
|
-
async getDistinctValues(objectName, column, objectType = "view") {
|
|
2178
|
+
async getDistinctValues(objectName, parameters, column, objectType = "view") {
|
|
2318
2179
|
if (!this.state.session) return err(new Error("Not logged in"));
|
|
2319
|
-
return getDistinctValues(this.requestor, objectName, column, objectType);
|
|
2180
|
+
return getDistinctValues(this.requestor, objectName, parameters, column, objectType);
|
|
2320
2181
|
}
|
|
2321
2182
|
async countRows(objectName, objectType) {
|
|
2322
2183
|
if (!this.state.session) return err(new Error("Not logged in"));
|
|
@@ -2361,19 +2222,8 @@ function createClient(config) {
|
|
|
2361
2222
|
}
|
|
2362
2223
|
return ok(new ADTClientImpl(config));
|
|
2363
2224
|
}
|
|
2364
|
-
|
|
2365
|
-
// src/core/config.ts
|
|
2366
|
-
import { readFileSync } from "fs";
|
|
2367
|
-
import { z as z2 } from "zod";
|
|
2368
|
-
var systemConfigSchema = z2.record(
|
|
2369
|
-
z2.string(),
|
|
2370
|
-
z2.object({
|
|
2371
|
-
adt: z2.string().url().optional(),
|
|
2372
|
-
odata: z2.string().url().optional(),
|
|
2373
|
-
instance_num: z2.string().optional()
|
|
2374
|
-
})
|
|
2375
|
-
);
|
|
2376
2225
|
export {
|
|
2226
|
+
buildSQLQuery,
|
|
2377
2227
|
createClient,
|
|
2378
2228
|
err,
|
|
2379
2229
|
ok
|