@saptools/cf-hana 0.3.4 → 0.3.5
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/CHANGELOG.md +5 -0
- package/dist/cli.js +85 -26
- package/dist/cli.js.map +1 -1
- package/dist/index.js +85 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -625,10 +625,10 @@ async function listColumns(connection, schema, table) {
|
|
|
625
625
|
}
|
|
626
626
|
|
|
627
627
|
// src/config.ts
|
|
628
|
-
var CLI_VERSION = "0.3.
|
|
628
|
+
var CLI_VERSION = "0.3.5";
|
|
629
629
|
var ENV_PREFIX = "CF_HANA";
|
|
630
|
-
var DEFAULT_QUERY_TIMEOUT_MS =
|
|
631
|
-
var DEFAULT_CONNECT_TIMEOUT_MS =
|
|
630
|
+
var DEFAULT_QUERY_TIMEOUT_MS = 6e4;
|
|
631
|
+
var DEFAULT_CONNECT_TIMEOUT_MS = 6e4;
|
|
632
632
|
var DEFAULT_POOL_MAX = 4;
|
|
633
633
|
var DEFAULT_POOL_IDLE_MS = 6e4;
|
|
634
634
|
var DEFAULT_AUTO_LIMIT = 100;
|
|
@@ -661,9 +661,9 @@ import { join as join2 } from "path";
|
|
|
661
661
|
import { promisify } from "util";
|
|
662
662
|
var execFileAsync = promisify(execFile);
|
|
663
663
|
var MAX_BUFFER = 16 * 1024 * 1024;
|
|
664
|
-
var DEFAULT_TIMEOUT_MS =
|
|
664
|
+
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
665
665
|
var CF_RETRY_ATTEMPTS = 3;
|
|
666
|
-
var CF_RETRY_BASE_DELAY_MS =
|
|
666
|
+
var CF_RETRY_BASE_DELAY_MS = 500;
|
|
667
667
|
var REGION_API_MAP = {
|
|
668
668
|
ae01: "https://api.cf.ae01.hana.ondemand.com",
|
|
669
669
|
ap01: "https://api.cf.ap01.hana.ondemand.com",
|
|
@@ -785,29 +785,45 @@ function buildEnv(ctx, overrides = {}) {
|
|
|
785
785
|
env["CF_HOME"] = ctx.cfHome;
|
|
786
786
|
return env;
|
|
787
787
|
}
|
|
788
|
-
async function
|
|
789
|
-
const { bin, argsPrefix } = resolveCfBin();
|
|
790
|
-
const env = buildEnv(ctx, overrides);
|
|
788
|
+
async function execWithRetries(bin, args, env, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
791
789
|
let lastErr;
|
|
792
790
|
for (let attempt = 0; attempt < CF_RETRY_ATTEMPTS; attempt++) {
|
|
793
791
|
try {
|
|
794
|
-
const { stdout } = await execFileAsync(bin,
|
|
792
|
+
const { stdout } = await execFileAsync(bin, args, {
|
|
795
793
|
env,
|
|
796
794
|
maxBuffer: MAX_BUFFER,
|
|
797
|
-
timeout:
|
|
795
|
+
timeout: timeoutMs,
|
|
796
|
+
killSignal: "SIGKILL"
|
|
798
797
|
});
|
|
799
798
|
return stdout;
|
|
800
799
|
} catch (err) {
|
|
801
800
|
lastErr = err;
|
|
801
|
+
const e = err;
|
|
802
|
+
const output = `${e.message ?? ""} ${e.stderr ? String(e.stderr) : ""}`.toLowerCase();
|
|
803
|
+
const isTimeout = e.killed === true;
|
|
804
|
+
const isNetworkFlake = output.includes("error performing request") || output.includes("timeout") || output.includes("connection reset") || output.includes("connection refused") || output.includes("eof") || output.includes("502 bad gateway") || output.includes("503 service unavailable") || output.includes("504 gateway timeout") || output.includes("dial tcp") || output.includes("no such host");
|
|
805
|
+
const isEnoent = e.code === "ENOENT";
|
|
806
|
+
if (isEnoent || !isTimeout && !isNetworkFlake) {
|
|
807
|
+
throw err;
|
|
808
|
+
}
|
|
802
809
|
if (attempt < CF_RETRY_ATTEMPTS - 1) {
|
|
803
810
|
await new Promise((r) => setTimeout(r, CF_RETRY_BASE_DELAY_MS * (attempt + 1)));
|
|
804
811
|
}
|
|
805
812
|
}
|
|
806
813
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
814
|
+
throw lastErr;
|
|
815
|
+
}
|
|
816
|
+
async function runCf(args, ctx, overrides = {}) {
|
|
817
|
+
const { bin, argsPrefix } = resolveCfBin();
|
|
818
|
+
const env = buildEnv(ctx, overrides);
|
|
819
|
+
try {
|
|
820
|
+
return await execWithRetries(bin, [...argsPrefix, ...args], env);
|
|
821
|
+
} catch (lastErr) {
|
|
822
|
+
const e = lastErr;
|
|
823
|
+
const detail = e?.stderr ? String(e.stderr) : e?.message ?? "";
|
|
824
|
+
const cmd = args[0] === "auth" ? "cf auth" : `cf ${args.join(" ")}`;
|
|
825
|
+
throw new Error(`${cmd} failed: ${detail}`.trim(), { cause: lastErr });
|
|
826
|
+
}
|
|
811
827
|
}
|
|
812
828
|
async function cfApi(apiEndpoint, ctx) {
|
|
813
829
|
await runCf(["api", apiEndpoint], ctx);
|
|
@@ -829,12 +845,7 @@ async function cfEnvDirect(appName) {
|
|
|
829
845
|
const env = { ...process.env };
|
|
830
846
|
delete env["SAP_EMAIL"];
|
|
831
847
|
delete env["SAP_PASSWORD"];
|
|
832
|
-
|
|
833
|
-
env,
|
|
834
|
-
maxBuffer: MAX_BUFFER,
|
|
835
|
-
timeout: DEFAULT_TIMEOUT_MS
|
|
836
|
-
});
|
|
837
|
-
return stdout;
|
|
848
|
+
return await execWithRetries(bin, [...argsPrefix, "env", appName], env);
|
|
838
849
|
}
|
|
839
850
|
async function readCurrentCfTarget() {
|
|
840
851
|
const { bin, argsPrefix } = resolveCfBin();
|
|
@@ -842,11 +853,7 @@ async function readCurrentCfTarget() {
|
|
|
842
853
|
delete env["SAP_EMAIL"];
|
|
843
854
|
delete env["SAP_PASSWORD"];
|
|
844
855
|
try {
|
|
845
|
-
const
|
|
846
|
-
env,
|
|
847
|
-
maxBuffer: MAX_BUFFER,
|
|
848
|
-
timeout: 1e4
|
|
849
|
-
});
|
|
856
|
+
const stdout = await execWithRetries(bin, [...argsPrefix, "target"], env);
|
|
850
857
|
return parseCfTargetOutput(stdout);
|
|
851
858
|
} catch {
|
|
852
859
|
return void 0;
|
|
@@ -1785,6 +1792,57 @@ function boundedRows(rows, requestedLimit) {
|
|
|
1785
1792
|
truncated: rows.length > requestedLimit
|
|
1786
1793
|
};
|
|
1787
1794
|
}
|
|
1795
|
+
function isBooleanColumn(column) {
|
|
1796
|
+
return column.typeName.trim().toUpperCase() === "BOOLEAN";
|
|
1797
|
+
}
|
|
1798
|
+
function normalizeBooleanCell(value) {
|
|
1799
|
+
if (value === 0) {
|
|
1800
|
+
return false;
|
|
1801
|
+
}
|
|
1802
|
+
if (value === 1) {
|
|
1803
|
+
return true;
|
|
1804
|
+
}
|
|
1805
|
+
if (typeof value !== "string") {
|
|
1806
|
+
return value;
|
|
1807
|
+
}
|
|
1808
|
+
const normalized = value.trim().toLowerCase();
|
|
1809
|
+
if (normalized === "0") {
|
|
1810
|
+
return false;
|
|
1811
|
+
}
|
|
1812
|
+
if (normalized === "1") {
|
|
1813
|
+
return true;
|
|
1814
|
+
}
|
|
1815
|
+
if (normalized === "false") {
|
|
1816
|
+
return false;
|
|
1817
|
+
}
|
|
1818
|
+
if (normalized === "true") {
|
|
1819
|
+
return true;
|
|
1820
|
+
}
|
|
1821
|
+
return value;
|
|
1822
|
+
}
|
|
1823
|
+
function normalizeBooleanRow(row, columns) {
|
|
1824
|
+
let normalizedRow;
|
|
1825
|
+
for (const column of columns) {
|
|
1826
|
+
const value = row[column.name];
|
|
1827
|
+
if (value === void 0) {
|
|
1828
|
+
continue;
|
|
1829
|
+
}
|
|
1830
|
+
const normalizedValue = normalizeBooleanCell(value);
|
|
1831
|
+
if (normalizedValue === value) {
|
|
1832
|
+
continue;
|
|
1833
|
+
}
|
|
1834
|
+
normalizedRow ??= { ...row };
|
|
1835
|
+
normalizedRow[column.name] = normalizedValue;
|
|
1836
|
+
}
|
|
1837
|
+
return normalizedRow ?? row;
|
|
1838
|
+
}
|
|
1839
|
+
function normalizeBooleanRows(rows, columns) {
|
|
1840
|
+
const booleanColumns = columns.filter(isBooleanColumn);
|
|
1841
|
+
if (booleanColumns.length === 0 || rows.length === 0) {
|
|
1842
|
+
return rows;
|
|
1843
|
+
}
|
|
1844
|
+
return rows.map((row) => normalizeBooleanRow(row, booleanColumns));
|
|
1845
|
+
}
|
|
1788
1846
|
async function withTimeout(work, timeoutMs, onTimeout) {
|
|
1789
1847
|
let timer;
|
|
1790
1848
|
const timeout = new Promise((_resolve, reject) => {
|
|
@@ -1870,7 +1928,8 @@ var Connection = class _Connection {
|
|
|
1870
1928
|
}
|
|
1871
1929
|
);
|
|
1872
1930
|
const elapsedMs = Date.now() - started;
|
|
1873
|
-
const
|
|
1931
|
+
const normalizedRows = normalizeBooleanRows(execResult.rows, execResult.columns);
|
|
1932
|
+
const selected = boundedRows(normalizedRows, limited.requestedLimit);
|
|
1874
1933
|
return {
|
|
1875
1934
|
rows: selected.rows,
|
|
1876
1935
|
columns: execResult.columns,
|