catalyst-relay 0.4.5 → 0.4.6

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/index.js CHANGED
@@ -255,7 +255,7 @@ var DEFAULT_SESSION_CONFIG = {
255
255
  };
256
256
 
257
257
  // src/core/session/login.ts
258
- async function fetchCsrfToken(state, request3) {
258
+ async function fetchCsrfToken(state, request4) {
259
259
  const endpoint = state.config.auth.type === "saml" ? "/sap/bc/adt/core/http/sessions" : "/sap/bc/adt/compatibility/graph";
260
260
  const contentType = state.config.auth.type === "saml" ? "application/vnd.sap.adt.core.http.session.v3+xml" : "application/xml";
261
261
  const headers = {
@@ -263,7 +263,7 @@ async function fetchCsrfToken(state, request3) {
263
263
  "Content-Type": contentType,
264
264
  "Accept": contentType
265
265
  };
266
- const [response, requestErr] = await request3({
266
+ const [response, requestErr] = await request4({
267
267
  method: "GET",
268
268
  path: endpoint,
269
269
  headers
@@ -313,8 +313,8 @@ function extractUsername(auth) {
313
313
  }
314
314
  }
315
315
  }
316
- async function login(state, request3) {
317
- const [token, tokenErr] = await fetchCsrfToken(state, request3);
316
+ async function login(state, request4) {
317
+ const [token, tokenErr] = await fetchCsrfToken(state, request4);
318
318
  if (tokenErr) {
319
319
  return err(new Error(`Login failed: ${tokenErr.message}`));
320
320
  }
@@ -328,8 +328,8 @@ async function login(state, request3) {
328
328
  state.session = session;
329
329
  return ok(session);
330
330
  }
331
- async function logout(state, request3) {
332
- const [response, requestErr] = await request3({
331
+ async function logout(state, request4) {
332
+ const [response, requestErr] = await request4({
333
333
  method: "POST",
334
334
  path: "/sap/public/bc/icf/logoff"
335
335
  });
@@ -344,11 +344,11 @@ async function logout(state, request3) {
344
344
  state.session = null;
345
345
  return ok(void 0);
346
346
  }
347
- async function sessionReset(state, request3) {
348
- await logout(state, request3);
347
+ async function sessionReset(state, request4) {
348
+ await logout(state, request4);
349
349
  state.csrfToken = null;
350
350
  state.session = null;
351
- const [, loginErr] = await login(state, request3);
351
+ const [, loginErr] = await login(state, request4);
352
352
  if (loginErr) {
353
353
  return err(loginErr);
354
354
  }
@@ -357,12 +357,12 @@ async function sessionReset(state, request3) {
357
357
 
358
358
  // src/core/session/refresh.ts
359
359
  var REENTRANCE_TICKET_PATH = "/sap/bc/adt/security/reentranceticket";
360
- async function refreshSession(state, request3) {
360
+ async function refreshSession(state, request4) {
361
361
  if (!state.session) {
362
362
  return err(new Error("Not logged in"));
363
363
  }
364
364
  debug("Fetching reentrance ticket to refresh session...");
365
- const [response, reqErr] = await request3({
365
+ const [response, reqErr] = await request4({
366
366
  method: "GET",
367
367
  path: REENTRANCE_TICKET_PATH,
368
368
  headers: { "Accept": "text/plain" }
@@ -1242,6 +1242,35 @@ async function previewData(client, query) {
1242
1242
  return ok(dataFrame);
1243
1243
  }
1244
1244
 
1245
+ // src/core/adt/data_extraction/freestyle.ts
1246
+ var DEFAULT_ROW_LIMIT = 100;
1247
+ async function freestyleQuery(client, sqlQuery, limit = DEFAULT_ROW_LIMIT) {
1248
+ debug(`Freestyle query: ${sqlQuery}`);
1249
+ const [response, requestErr] = await client.request({
1250
+ method: "POST",
1251
+ path: "/sap/bc/adt/datapreview/freestyle",
1252
+ params: {
1253
+ "rowNumber": limit
1254
+ },
1255
+ headers: {
1256
+ "Accept": "application/xml, application/vnd.sap.adt.datapreview.table.v1+xml",
1257
+ "Content-Type": "text/plain"
1258
+ },
1259
+ body: sqlQuery
1260
+ });
1261
+ if (requestErr) return err(requestErr);
1262
+ if (!response.ok) {
1263
+ const text2 = await response.text();
1264
+ debug(`Freestyle query error response: ${text2.substring(0, 500)}`);
1265
+ const errorMsg = extractError(text2);
1266
+ return err(new Error(`Freestyle query failed: ${errorMsg}`));
1267
+ }
1268
+ const text = await response.text();
1269
+ const [dataFrame, parseErr] = parseDataPreview(text, limit, true);
1270
+ if (parseErr) return err(parseErr);
1271
+ return ok(dataFrame);
1272
+ }
1273
+
1245
1274
  // src/core/adt/data_extraction/queryBuilder.ts
1246
1275
  function quoteString(value) {
1247
1276
  return typeof value == "string" ? "'" + value + "'" : "" + value;
@@ -1316,15 +1345,10 @@ from ${query.objectName}${parametersToSQLParams(parameters)} as main
1316
1345
 
1317
1346
  // src/core/adt/data_extraction/distinct.ts
1318
1347
  var MAX_ROW_COUNT = 5e4;
1319
- async function getDistinctValues(client, objectName, parameters, column, objectType = "view") {
1348
+ async function getDistinctValues(client, objectName, parameters, column, _objectType = "view") {
1320
1349
  const columnName = column.toUpperCase();
1321
- const sqlQuery = `SELECT ${columnName} AS value, COUNT(*) AS ValueCount FROM ${objectName}${parametersToSQLParams(parameters)} GROUP BY ${columnName} ORDER BY ValueCount DESCENDING`;
1322
- const [dataFrame, error] = await previewData(client, {
1323
- objectName,
1324
- objectType,
1325
- sqlQuery,
1326
- limit: MAX_ROW_COUNT
1327
- });
1350
+ const sqlQuery = `SELECT ${columnName} AS value, COUNT(*) AS value_count FROM ${objectName}${parametersToSQLParams(parameters)} GROUP BY ${columnName} ORDER BY value_count DESCENDING`;
1351
+ const [dataFrame, error] = await freestyleQuery(client, sqlQuery, MAX_ROW_COUNT);
1328
1352
  if (error) {
1329
1353
  return err(new Error(`Distinct values query failed: ${error.message}`));
1330
1354
  }
@@ -1336,14 +1360,9 @@ async function getDistinctValues(client, objectName, parameters, column, objectT
1336
1360
  }
1337
1361
 
1338
1362
  // src/core/adt/data_extraction/count.ts
1339
- async function countRows(client, objectName, objectType) {
1340
- const sqlQuery = `SELECT COUNT(*) AS count FROM ${objectName}`;
1341
- const [dataFrame, error] = await previewData(client, {
1342
- objectName,
1343
- objectType,
1344
- sqlQuery,
1345
- limit: 1
1346
- });
1363
+ async function countRows(client, objectName, _objectType) {
1364
+ const sqlQuery = `SELECT COUNT(*) AS row_count FROM ${objectName}`;
1365
+ const [dataFrame, error] = await freestyleQuery(client, sqlQuery, 1);
1347
1366
  if (error) {
1348
1367
  return err(new Error(`Row count query failed: ${error.message}`));
1349
1368
  }
@@ -2246,13 +2265,22 @@ function createAuthStrategy(options) {
2246
2265
  }
2247
2266
 
2248
2267
  // src/core/client.ts
2268
+ var http = __toESM(require("http"));
2249
2269
  var https2 = __toESM(require("https"));
2250
- async function httpsRequest2(url, options) {
2270
+ var MAX_REDIRECTS = 5;
2271
+ var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
2272
+ async function httpRequest(url, options, redirectCount = 0) {
2273
+ if (redirectCount > MAX_REDIRECTS) {
2274
+ throw new Error(`Too many redirects (max ${MAX_REDIRECTS})`);
2275
+ }
2251
2276
  const urlObj = new URL(url);
2277
+ const isHttps = urlObj.protocol === "https:";
2278
+ const requestFn = isHttps ? https2.request : http.request;
2279
+ const defaultPort = isHttps ? 443 : 80;
2252
2280
  return new Promise((resolve, reject) => {
2253
- const req = https2.request({
2281
+ const req = requestFn({
2254
2282
  hostname: urlObj.hostname,
2255
- port: urlObj.port || 443,
2283
+ port: urlObj.port || defaultPort,
2256
2284
  path: urlObj.pathname + urlObj.search,
2257
2285
  method: options.method,
2258
2286
  headers: options.headers,
@@ -2261,6 +2289,14 @@ async function httpsRequest2(url, options) {
2261
2289
  rejectUnauthorized: options.rejectUnauthorized ?? true,
2262
2290
  timeout: options.timeout
2263
2291
  }, (res) => {
2292
+ const statusCode = res.statusCode || 0;
2293
+ if (REDIRECT_STATUSES.has(statusCode) && res.headers.location) {
2294
+ const redirectUrl = new URL(res.headers.location, url).toString();
2295
+ const redirectMethod = statusCode === 303 ? "GET" : options.method;
2296
+ const redirectBody = statusCode === 303 ? void 0 : options.body;
2297
+ httpRequest(redirectUrl, { ...options, method: redirectMethod, body: redirectBody }, redirectCount + 1).then(resolve).catch(reject);
2298
+ return;
2299
+ }
2264
2300
  const chunks = [];
2265
2301
  res.on("data", (chunk) => chunks.push(chunk));
2266
2302
  res.on("end", () => {
@@ -2276,7 +2312,7 @@ async function httpsRequest2(url, options) {
2276
2312
  }
2277
2313
  }
2278
2314
  resolve(new Response(body, {
2279
- status: res.statusCode || 0,
2315
+ status: statusCode,
2280
2316
  statusText: res.statusMessage || "",
2281
2317
  headers
2282
2318
  }));
@@ -2395,7 +2431,7 @@ var ADTClientImpl = class {
2395
2431
  try {
2396
2432
  debug(`Fetching URL: ${url}`);
2397
2433
  debug(`mTLS: ${!!this.ssoCerts}, insecure: ${config.insecure}`);
2398
- const response = await httpsRequest2(url, {
2434
+ const response = await httpRequest(url, {
2399
2435
  method,
2400
2436
  headers,
2401
2437
  body,
@@ -2418,7 +2454,7 @@ var ADTClientImpl = class {
2418
2454
  headers["Cookie"] = retryCookieHeader;
2419
2455
  }
2420
2456
  debug(`Retrying with new CSRF token: ${newToken.substring(0, 20)}...`);
2421
- const retryResponse = await httpsRequest2(url, {
2457
+ const retryResponse = await httpRequest(url, {
2422
2458
  method,
2423
2459
  headers,
2424
2460
  body,