quickbase-js 0.1.0-beta.9 → 1.0.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/README.md +270 -60
- package/dist/esm/quickbase.d.ts +2553 -3220
- package/dist/esm/quickbase.js +1545 -3443
- package/dist/esm/quickbase.js.map +1 -1
- package/dist/esm/quickbase.min.js +1 -1
- package/dist/esm/quickbase.min.js.map +1 -1
- package/dist/umd/quickbase.umd.js +1544 -3625
- package/dist/umd/quickbase.umd.js.map +1 -1
- package/dist/umd/quickbase.umd.min.js +1 -1
- package/dist/umd/quickbase.umd.min.js.map +1 -1
- package/package.json +25 -12
- package/CHANGELOG.md +0 -184
- package/build-common.js +0 -131
- package/build-esm.js +0 -71
- package/build-umd.js +0 -69
- package/examples/quicbase-jsCDN.html +0 -36
- package/examples/react.js +0 -48
- package/examples/vanillajs.html +0 -47
- package/open-api/fix-spec-main.ts +0 -97
- package/open-api/generate-override-report.ts +0 -214
- package/open-api/generate-unified-interface.ts +0 -336
- package/open-api/output/override-report.html +0 -11
- package/open-api/output/override-report.json +0 -14395
- package/open-api/output/quickbase-fixed.json +0 -11191
- package/open-api/regenerate-client.ts +0 -116
- package/open-api/schema/enhance-general.ts +0 -100
- package/open-api/schema/fix-arrays.ts +0 -69
- package/open-api/schema/tags/apps.ts +0 -90
- package/open-api/schema/tags/fields.ts +0 -471
- package/open-api/schema/tags/groups.ts +0 -51
- package/open-api/schema/tags/index.ts +0 -66
- package/open-api/schema/tags/records.ts +0 -115
- package/open-api/schema/tags/tables.ts +0 -211
- package/open-api/schema/tags/users.ts +0 -47
- package/open-api/types/spec.ts +0 -42
- package/open-api/utils/generateJsDoc.ts +0 -96
- package/open-api/utils/infer-schema.ts +0 -40
- package/open-api/utils/naming.ts +0 -10
- package/playwright.config.ts +0 -37
- package/rollup.config.js +0 -114
- package/specs/QuickBase_RESTful_API_2025-03-04T06_22_39.725Z.json +0 -46878
- package/specs/split/apps.json +0 -1315
- package/specs/split/auth.json +0 -191
- package/specs/split/fields.json +0 -3869
- package/specs/split/records.json +0 -754
- package/specs/split/reports.json +0 -619
- package/specs/split/tables.json +0 -1605
- package/src/RateLimitError.ts +0 -12
- package/src/ThrottleBucket.ts +0 -41
- package/src/authorizationStrategy.ts +0 -246
- package/src/generated-unified/QuickbaseClient.ts +0 -1049
- package/src/generated-unified/missing-types-report.json +0 -3
- package/src/index.ts +0 -1
- package/src/invokeMethod.ts +0 -242
- package/src/quickbaseClient.ts +0 -271
- package/src/rateLimiter.ts +0 -25
- package/src/tokenCache.ts +0 -35
- package/src/utils.ts +0 -7
- package/test-esm.js +0 -1
- package/test-results.json +0 -2050
- package/test-umd.js +0 -1
- package/test.ts +0 -1
- package/tests/playwright/qb/auth/cacheSwitching.test.ts +0 -159
- package/tests/playwright/qb/auth/fetchTempToken401.test.ts +0 -219
- package/tests/playwright/qb/auth/getAppWithTempToken.test.ts +0 -162
- package/tests/playwright/qb/auth/getAppWithUserToken.test.ts +0 -153
- package/tests/playwright/qb/auth/getTempToken.test.ts +0 -152
- package/tests/playwright/qb/auth/tempTokenPrefetch.test.ts +0 -179
- package/tests/playwright/qb/auth/tempTokenRenewal.test.ts +0 -133
- package/tests/setup.ts +0 -53
- package/tests/vitest/qb/apps/copyApp.test.ts +0 -76
- package/tests/vitest/qb/apps/createApp.test.ts +0 -64
- package/tests/vitest/qb/apps/deleteApp.test.ts +0 -76
- package/tests/vitest/qb/apps/getApp.test.ts +0 -44
- package/tests/vitest/qb/apps/getAppEvents.test.ts +0 -67
- package/tests/vitest/qb/apps/updateApp.test.ts +0 -58
- package/tests/vitest/qb/auth/mockQuickbaseServer.ts +0 -60
- package/tests/vitest/qb/auth/retryUserToken401.test.ts +0 -73
- package/tests/vitest/qb/auth/ssoTokenIntegrationWithServer.test.ts +0 -65
- package/tests/vitest/qb/fields/createField.test.ts +0 -77
- package/tests/vitest/qb/fields/deleteFields.test.ts +0 -83
- package/tests/vitest/qb/fields/getField.test.ts +0 -85
- package/tests/vitest/qb/fields/getFieldUsage.test.ts +0 -82
- package/tests/vitest/qb/fields/getFields.test.ts +0 -232
- package/tests/vitest/qb/fields/getFieldsUsage.test.ts +0 -77
- package/tests/vitest/qb/fields/updateField.test.ts +0 -93
- package/tests/vitest/qb/formulas/runFormula.test.ts +0 -96
- package/tests/vitest/qb/index.ts +0 -29
- package/tests/vitest/qb/records/deleteRecords.test.ts +0 -110
- package/tests/vitest/qb/records/runQuery.test.ts +0 -146
- package/tests/vitest/qb/records/upsert.test.ts +0 -102
- package/tests/vitest/qb/tables/createRelationship.test.ts +0 -102
- package/tests/vitest/qb/tables/createTable.test.ts +0 -55
- package/tests/vitest/qb/tables/deleteRelationship.test.ts +0 -143
- package/tests/vitest/qb/tables/deleteTable.test.ts +0 -50
- package/tests/vitest/qb/tables/getAppTables.test.ts +0 -46
- package/tests/vitest/qb/tables/getRelationships.test.ts +0 -80
- package/tests/vitest/qb/tables/getTable.test.ts +0 -26
- package/tests/vitest/qb/tables/updateRelationship.test.ts +0 -250
- package/tests/vitest/qb/tables/updateTable.test.ts +0 -48
- package/tests/vitest/unit/ThrottleBucket.test.ts +0 -95
- package/tests/vitest/unit/apps/copyApp.test.ts +0 -581
- package/tests/vitest/unit/apps/createApp.test.ts +0 -186
- package/tests/vitest/unit/apps/deleteApp.test.ts +0 -216
- package/tests/vitest/unit/apps/getApp.test.ts +0 -128
- package/tests/vitest/unit/apps/getAppEvents.test.ts +0 -193
- package/tests/vitest/unit/apps/updateApp.test.ts +0 -412
- package/tests/vitest/unit/auth/fetchTempToken401.test.ts +0 -63
- package/tests/vitest/unit/auth/getTempToken.test.ts +0 -119
- package/tests/vitest/unit/auth/retryTempToken401.test.ts +0 -95
- package/tests/vitest/unit/auth/retryTempTokenMaxRetries.test.ts +0 -235
- package/tests/vitest/unit/auth/retryUserToken401.test.ts +0 -69
- package/tests/vitest/unit/auth/ssoTokenRefresh.test.ts +0 -325
- package/tests/vitest/unit/fields/createField.test.ts +0 -451
- package/tests/vitest/unit/fields/deleteFields.test.ts +0 -141
- package/tests/vitest/unit/fields/getField.test.ts +0 -189
- package/tests/vitest/unit/fields/getFieldUsage.test.ts +0 -180
- package/tests/vitest/unit/fields/getFields.test.ts +0 -122
- package/tests/vitest/unit/fields/getFieldsUsage.test.ts +0 -160
- package/tests/vitest/unit/fields/updateField.test.ts +0 -285
- package/tests/vitest/unit/formulas/runFormula.test.ts +0 -205
- package/tests/vitest/unit/index.ts +0 -34
- package/tests/vitest/unit/inferHttpMethod.test.ts +0 -131
- package/tests/vitest/unit/rateLimit.test.ts +0 -191
- package/tests/vitest/unit/records/deleteRecords.test.ts +0 -112
- package/tests/vitest/unit/records/runQuery.test.ts +0 -102
- package/tests/vitest/unit/records/upsert.test.ts +0 -234
- package/tests/vitest/unit/tables/createRelationship.test.ts +0 -212
- package/tests/vitest/unit/tables/createTable.test.ts +0 -70
- package/tests/vitest/unit/tables/deleteRelationship.test.ts +0 -97
- package/tests/vitest/unit/tables/deleteTable.test.ts +0 -77
- package/tests/vitest/unit/tables/getAppTables.test.ts +0 -85
- package/tests/vitest/unit/tables/getRelationships.test.ts +0 -259
- package/tests/vitest/unit/tables/getTable.test.ts +0 -233
- package/tests/vitest/unit/tables/updateRelationship.test.ts +0 -307
- package/tests/vitest/unit/tables/updateTable.test.ts +0 -80
- package/tests/vitest/unit/tokenLifespan.test.ts +0 -204
- package/tests/vitest/unit/umd.test.ts +0 -8
- package/text.txt +0 -104
- package/tools/log-gen.ts +0 -244
- package/tools/log-gen.yaml +0 -5424
- package/tools/openapi-jars/openapi-generator-cli-7.12.0.jar +0 -0
- package/tsconfig.build.json +0 -17
- package/tsconfig.json +0 -32
- package/versionrc.json +0 -9
- package/vitest.config.ts +0 -27
package/examples/vanillajs.html
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
|
|
4
|
-
<head>
|
|
5
|
-
<title>Quickbase Test</title>
|
|
6
|
-
</head>
|
|
7
|
-
|
|
8
|
-
<body>
|
|
9
|
-
<div>
|
|
10
|
-
<h1>Quickbase Test</h1>
|
|
11
|
-
<p id="result">Loading...</p>
|
|
12
|
-
</div>
|
|
13
|
-
<script type="module">
|
|
14
|
-
import { quickbase } from 'quickbase-js';
|
|
15
|
-
// Initalize the QuickBase client
|
|
16
|
-
const qb = quickbase({
|
|
17
|
-
realm: 'your-realm', // Replace with actual QuickBase realm
|
|
18
|
-
// ## ------------------------------
|
|
19
|
-
// ## Authentication Options
|
|
20
|
-
// ## ----------------------------
|
|
21
|
-
// ## OPTION 1: User Token Authentication
|
|
22
|
-
// ## - Use this if you have a QuickBase user token (get it from "My Profile" > "Manage User Tokens")
|
|
23
|
-
// ## - Works in Node.js or browsers, ideal for standalone apps or testing outside QuickBase
|
|
24
|
-
// ## - Uncomment the line below and replace with your token; comment out 'useTempTokens'
|
|
25
|
-
// userToken: "your-user-token",
|
|
26
|
-
|
|
27
|
-
// ## ----------------------------
|
|
28
|
-
// ## OPTION 2: Temporary Token Authentication
|
|
29
|
-
// ## - Use this for QuickBase code pages, leveraging the browser’s authenticated session
|
|
30
|
-
// ## - No user token needed; requires running in a QuickBase browser context
|
|
31
|
-
// ## - Uncomment the line below and comment out 'userToken' if using this option
|
|
32
|
-
// useTempTokens: true,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// Fetch the app
|
|
36
|
-
qb.getApp({ appId: 'your-app-id' }) // Replace with actual app ID
|
|
37
|
-
.then((app) => {
|
|
38
|
-
document.getElementById('result').textContent = `App Name: ${app.name}`;
|
|
39
|
-
})
|
|
40
|
-
.catch((err) => {
|
|
41
|
-
console.error('Error fetching app:', err);
|
|
42
|
-
document.getElementById('result').textContent = err.message || 'Failed to load app';
|
|
43
|
-
});
|
|
44
|
-
</script>
|
|
45
|
-
</body>
|
|
46
|
-
|
|
47
|
-
</html>
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { promises as fs } from "fs";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { fileURLToPath } from "url";
|
|
5
|
-
import * as glob from "glob";
|
|
6
|
-
import { Spec } from "./types/spec.ts";
|
|
7
|
-
import { toCamelCase } from "./utils/naming.ts";
|
|
8
|
-
|
|
9
|
-
async function fixQuickBaseSpec(): Promise<void> {
|
|
10
|
-
const CODEGEN_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
-
const SPECS_DIR = path.join(CODEGEN_DIR, "..", "specs");
|
|
12
|
-
const OUTPUT_DIR = path.join(CODEGEN_DIR, "output");
|
|
13
|
-
console.log("Finding latest QuickBase RESTful API spec...");
|
|
14
|
-
const specFiles = glob.sync(path.join(SPECS_DIR, "QuickBase_RESTful_*.json"));
|
|
15
|
-
console.log("Spec files:", specFiles);
|
|
16
|
-
|
|
17
|
-
if (specFiles.length === 0) {
|
|
18
|
-
throw new Error(
|
|
19
|
-
"No QuickBase_RESTful_*.json files found in specs/ folder."
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const inputFile = specFiles.sort().pop() as string;
|
|
24
|
-
const outputFile = path.join(OUTPUT_DIR, "quickbase-fixed.json");
|
|
25
|
-
|
|
26
|
-
console.log(`Reading ${path.basename(inputFile)} from specs/...`);
|
|
27
|
-
const specContent = await fs.readFile(inputFile, "utf8");
|
|
28
|
-
console.log("File read successfully");
|
|
29
|
-
const spec: Spec = JSON.parse(specContent);
|
|
30
|
-
console.log("JSON parsed successfully");
|
|
31
|
-
|
|
32
|
-
if (!spec.definitions || typeof spec.definitions !== "object") {
|
|
33
|
-
spec.definitions = {};
|
|
34
|
-
}
|
|
35
|
-
console.log("Spec definitions after initialization:", spec.definitions);
|
|
36
|
-
|
|
37
|
-
console.log("Fixing parameters...");
|
|
38
|
-
for (const pathKey in spec.paths) {
|
|
39
|
-
for (const method in spec.paths[pathKey]) {
|
|
40
|
-
const operation = spec.paths[pathKey][method];
|
|
41
|
-
if (operation.parameters) {
|
|
42
|
-
operation.parameters = operation.parameters
|
|
43
|
-
.filter(
|
|
44
|
-
(param) =>
|
|
45
|
-
!["QB-Realm-Hostname", "Authorization", "User-Agent"].includes(
|
|
46
|
-
param.name
|
|
47
|
-
)
|
|
48
|
-
)
|
|
49
|
-
.map((param) => {
|
|
50
|
-
param.name = toCamelCase(param.name);
|
|
51
|
-
if ("example" in param) delete param.example;
|
|
52
|
-
if ("schema" in param && param.in !== "body") delete param.schema;
|
|
53
|
-
if (!param.type && param.in !== "body") {
|
|
54
|
-
param.type = param.name.includes("Id") ? "string" : "string";
|
|
55
|
-
console.log(
|
|
56
|
-
`Set default type 'string' for ${pathKey}(${method}).${param.name}`
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
return param;
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
console.log("Spec definitions before enhanceTags:", spec.definitions);
|
|
66
|
-
console.log("Enhancing raw spec with tags...");
|
|
67
|
-
const { enhanceTags } = await import("./schema/tags/index.ts"); // Updated to index.ts
|
|
68
|
-
enhanceTags(spec);
|
|
69
|
-
console.log("Enhancing raw spec with general enhancements...");
|
|
70
|
-
const { enhanceGeneral } = await import("./schema/enhance-general.ts");
|
|
71
|
-
enhanceGeneral(spec);
|
|
72
|
-
console.log("Fixing array schemas...");
|
|
73
|
-
const { fixArrays } = await import("./schema/fix-arrays.ts");
|
|
74
|
-
fixArrays(spec);
|
|
75
|
-
|
|
76
|
-
console.log("Removing unexpected top-level attributes...");
|
|
77
|
-
delete spec.operations;
|
|
78
|
-
delete spec.groups;
|
|
79
|
-
delete spec.components;
|
|
80
|
-
|
|
81
|
-
console.log(`Writing fixed spec to ${path.basename(outputFile)}...`);
|
|
82
|
-
await fs.mkdir(OUTPUT_DIR, { recursive: true });
|
|
83
|
-
await fs.writeFile(outputFile, JSON.stringify(spec, null, 2), "utf8");
|
|
84
|
-
console.log("Spec fixed successfully! Output written to:", outputFile);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async function main() {
|
|
88
|
-
try {
|
|
89
|
-
console.log("Starting...");
|
|
90
|
-
await fixQuickBaseSpec();
|
|
91
|
-
} catch (error) {
|
|
92
|
-
console.error("Error:", error);
|
|
93
|
-
process.exit(1);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
main();
|
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { promises as fs } from "fs";
|
|
4
|
-
import * as path from "path";
|
|
5
|
-
import { fileURLToPath } from "url";
|
|
6
|
-
import fastJsonPatch, { AddOperation } from "fast-json-patch";
|
|
7
|
-
import { Spec } from "./types/spec";
|
|
8
|
-
|
|
9
|
-
const { compare, applyPatch } = fastJsonPatch;
|
|
10
|
-
|
|
11
|
-
// Basic CSS for readability
|
|
12
|
-
const HTML_STYLE = `
|
|
13
|
-
<style>
|
|
14
|
-
.jsondiffpatch-delta { font-family: monospace; }
|
|
15
|
-
.jsondiffpatch-added { background-color: #e6ffe6; color: #006400; }
|
|
16
|
-
.jsondiffpatch-deleted { background-color: #ffe6e6; color: #8b0000; text-decoration: line-through; }
|
|
17
|
-
.jsondiffpatch-modified { background-color: #fff3e6; }
|
|
18
|
-
.jsondiffpatch-unchanged { color: #666; }
|
|
19
|
-
.jsondiffpatch-key { font-weight: bold; }
|
|
20
|
-
.jsondiffpatch-tags { color: purple; font-style: italic; }
|
|
21
|
-
</style>
|
|
22
|
-
`;
|
|
23
|
-
|
|
24
|
-
// Helper to decode JSON Pointer paths
|
|
25
|
-
function decodeJsonPointer(jsonPointerSegment: string): string {
|
|
26
|
-
// Split the full path and decode each segment
|
|
27
|
-
const segments = jsonPointerSegment.split("/").filter(Boolean);
|
|
28
|
-
const decodedSegments = segments.map((seg) =>
|
|
29
|
-
seg.replace("~1", "/").replace("~0", "~")
|
|
30
|
-
);
|
|
31
|
-
// Remove "paths" prefix and join with a single leading slash
|
|
32
|
-
const pathIndex = decodedSegments.indexOf("paths");
|
|
33
|
-
const relevantSegments =
|
|
34
|
-
pathIndex !== -1 && pathIndex + 1 < decodedSegments.length
|
|
35
|
-
? decodedSegments.slice(pathIndex + 1)
|
|
36
|
-
: decodedSegments;
|
|
37
|
-
return "/" + relevantSegments.join("/"); // Ensure single leading slash
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Enhanced HTML formatter with clean paths, tags, and old vs. new values
|
|
41
|
-
function formatPatchToHtml(patch: any[], spec: Spec): string {
|
|
42
|
-
let html = `<html><head><title>Override Report Diff</title>${HTML_STYLE}</head><body><h1>Override Report Diff</h1>`;
|
|
43
|
-
patch.forEach((op) => {
|
|
44
|
-
const pathParts = op.path.split("/").filter(Boolean);
|
|
45
|
-
const lastPart = pathParts[pathParts.length - 1];
|
|
46
|
-
let oldValue = "N/A";
|
|
47
|
-
let context = decodeJsonPointer(op.path);
|
|
48
|
-
let method = op.method ? op.method.toUpperCase() : "";
|
|
49
|
-
|
|
50
|
-
if (method) context = context.replace(`/${method}`, "") + ` [${method}]`;
|
|
51
|
-
|
|
52
|
-
if (op.op === "replace" || op.op === "remove") {
|
|
53
|
-
const reversePatch: AddOperation<any>[] = [
|
|
54
|
-
{ op: "add", path: op.path, value: op.value },
|
|
55
|
-
];
|
|
56
|
-
const tempSpec = JSON.parse(JSON.stringify(spec));
|
|
57
|
-
applyPatch(tempSpec, reversePatch);
|
|
58
|
-
const pathSegments = op.path.split("/").slice(1);
|
|
59
|
-
oldValue =
|
|
60
|
-
pathSegments.reduce((obj: any, key: string) => obj?.[key], tempSpec) ||
|
|
61
|
-
"N/A";
|
|
62
|
-
oldValue = JSON.stringify(oldValue);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const tagDisplay =
|
|
66
|
-
op.tags && op.tags.length > 0 ? ` (Tags: ${op.tags.join(", ")})` : "";
|
|
67
|
-
|
|
68
|
-
if (op.op === "replace") {
|
|
69
|
-
html += `<h2>Path: ${context}${tagDisplay}</h2><p>Modified: <span class="jsondiffpatch-deleted">${oldValue}</span> → <span class="jsondiffpatch-added">${JSON.stringify(
|
|
70
|
-
op.value
|
|
71
|
-
)}</span></p>`;
|
|
72
|
-
} else if (op.op === "add") {
|
|
73
|
-
html += `<h2>Path: ${context}${tagDisplay}</h2><p>Added: <span class="jsondiffpatch-added">${JSON.stringify(
|
|
74
|
-
op.value
|
|
75
|
-
)}</span></p>`;
|
|
76
|
-
} else if (op.op === "remove") {
|
|
77
|
-
html += `<h2>Path: ${context}${tagDisplay}</h2><p>Removed: <span class="jsondiffpatch-deleted">${oldValue}</span></p>`;
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
html += "</body></html>";
|
|
81
|
-
return html;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async function generateOverrideReport(): Promise<void> {
|
|
85
|
-
try {
|
|
86
|
-
const CODEGEN_DIR: string = path.dirname(fileURLToPath(import.meta.url));
|
|
87
|
-
const SPECS_DIR: string = path.join(CODEGEN_DIR, "..", "specs");
|
|
88
|
-
const OUTPUT_DIR: string = path.join(CODEGEN_DIR, "output");
|
|
89
|
-
const RAW_SPEC: string = path.join(
|
|
90
|
-
SPECS_DIR,
|
|
91
|
-
"QuickBase_RESTful_API_2025-03-04T06_22_39.725Z.json"
|
|
92
|
-
);
|
|
93
|
-
const FIXED_SPEC: string = path.join(OUTPUT_DIR, "quickbase-fixed.json");
|
|
94
|
-
const JSON_OUTPUT_FILE: string = path.join(
|
|
95
|
-
OUTPUT_DIR,
|
|
96
|
-
"override-report.json"
|
|
97
|
-
);
|
|
98
|
-
const HTML_OUTPUT_FILE: string = path.join(
|
|
99
|
-
OUTPUT_DIR,
|
|
100
|
-
"override-report.html"
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
console.log("Cleaning up old override report...");
|
|
104
|
-
if (
|
|
105
|
-
await fs
|
|
106
|
-
.stat(JSON_OUTPUT_FILE)
|
|
107
|
-
.then(() => true)
|
|
108
|
-
.catch(() => false)
|
|
109
|
-
) {
|
|
110
|
-
await fs.unlink(JSON_OUTPUT_FILE);
|
|
111
|
-
console.log(`Removed ${JSON_OUTPUT_FILE}`);
|
|
112
|
-
}
|
|
113
|
-
if (
|
|
114
|
-
await fs
|
|
115
|
-
.stat(HTML_OUTPUT_FILE)
|
|
116
|
-
.then(() => true)
|
|
117
|
-
.catch(() => false)
|
|
118
|
-
) {
|
|
119
|
-
await fs.unlink(HTML_OUTPUT_FILE);
|
|
120
|
-
console.log(`Removed ${HTML_OUTPUT_FILE}`);
|
|
121
|
-
} else {
|
|
122
|
-
console.log("No old override reports found to remove.");
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (
|
|
126
|
-
!(await fs
|
|
127
|
-
.stat(RAW_SPEC)
|
|
128
|
-
.then(() => true)
|
|
129
|
-
.catch(() => false))
|
|
130
|
-
) {
|
|
131
|
-
console.error(`Raw spec ${RAW_SPEC} not found.`);
|
|
132
|
-
process.exit(1);
|
|
133
|
-
}
|
|
134
|
-
if (
|
|
135
|
-
!(await fs
|
|
136
|
-
.stat(FIXED_SPEC)
|
|
137
|
-
.then(() => true)
|
|
138
|
-
.catch(() => false))
|
|
139
|
-
) {
|
|
140
|
-
console.error(
|
|
141
|
-
`Fixed spec ${FIXED_SPEC} not found. Run 'npm run fix-spec' first.`
|
|
142
|
-
);
|
|
143
|
-
process.exit(1);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
console.log(`Reading raw spec from ${RAW_SPEC}...`);
|
|
147
|
-
const rawContent = await fs.readFile(RAW_SPEC, "utf8");
|
|
148
|
-
const rawSpec: Spec = JSON.parse(rawContent);
|
|
149
|
-
|
|
150
|
-
console.log(`Reading fixed spec from ${FIXED_SPEC}...`);
|
|
151
|
-
const fixedContent = await fs.readFile(FIXED_SPEC, "utf8");
|
|
152
|
-
const fixedSpec: Spec = JSON.parse(fixedContent);
|
|
153
|
-
|
|
154
|
-
console.log("Generating patch...");
|
|
155
|
-
const patch = compare(rawSpec, fixedSpec);
|
|
156
|
-
|
|
157
|
-
// Enhance patch with decoded paths and tags inferred from parent operation
|
|
158
|
-
const enhancedPatch = patch.map((op) => {
|
|
159
|
-
let decodedPath = decodeJsonPointer(op.path);
|
|
160
|
-
let tags: string[] = [];
|
|
161
|
-
let method: string = "";
|
|
162
|
-
|
|
163
|
-
if (op.path.startsWith("/paths")) {
|
|
164
|
-
const pathParts = op.path.split("/").filter(Boolean);
|
|
165
|
-
if (
|
|
166
|
-
pathParts.length >= 3 &&
|
|
167
|
-
["get", "post", "put", "delete"].includes(pathParts[2].toLowerCase())
|
|
168
|
-
) {
|
|
169
|
-
const fullPathSegment = "/paths/" + pathParts[1];
|
|
170
|
-
const basePath = decodeJsonPointer(fullPathSegment);
|
|
171
|
-
method = pathParts[2];
|
|
172
|
-
console.log(`Debug: basePath=${basePath}, method=${method}`); // Debug log
|
|
173
|
-
if (rawSpec.paths[basePath]?.[method]?.tags) {
|
|
174
|
-
tags = rawSpec.paths[basePath][method].tags || [];
|
|
175
|
-
} else if (rawSpec.paths[basePath]?.[method]) {
|
|
176
|
-
console.log(`No tags found for ${basePath} [${method}]`);
|
|
177
|
-
} else {
|
|
178
|
-
console.log(`Operation not found: ${basePath} [${method}]`);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return {
|
|
184
|
-
...op,
|
|
185
|
-
decodedPath,
|
|
186
|
-
tags: tags.length > 0 ? tags : undefined,
|
|
187
|
-
method,
|
|
188
|
-
};
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
console.log("Writing JSON override report...");
|
|
192
|
-
await fs.mkdir(OUTPUT_DIR, { recursive: true });
|
|
193
|
-
await fs.writeFile(
|
|
194
|
-
JSON_OUTPUT_FILE,
|
|
195
|
-
JSON.stringify(enhancedPatch, null, 2),
|
|
196
|
-
"utf8"
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
console.log("Writing HTML override report...");
|
|
200
|
-
const htmlContent = formatPatchToHtml(enhancedPatch, rawSpec);
|
|
201
|
-
await fs.writeFile(HTML_OUTPUT_FILE, htmlContent, "utf8");
|
|
202
|
-
|
|
203
|
-
console.log("Override report generated successfully!");
|
|
204
|
-
} catch (error) {
|
|
205
|
-
console.error("Failed to generate override report:", error);
|
|
206
|
-
process.exit(1);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
async function main() {
|
|
211
|
-
await generateOverrideReport();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
main();
|
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
console.log("Script started");
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
readFileSync,
|
|
6
|
-
writeFileSync,
|
|
7
|
-
mkdirSync,
|
|
8
|
-
existsSync,
|
|
9
|
-
readdirSync,
|
|
10
|
-
} from "fs";
|
|
11
|
-
console.log("FS modules imported");
|
|
12
|
-
|
|
13
|
-
import { join, dirname } from "path";
|
|
14
|
-
console.log("Path modules imported");
|
|
15
|
-
|
|
16
|
-
import { fileURLToPath } from "url";
|
|
17
|
-
console.log("URL module imported");
|
|
18
|
-
|
|
19
|
-
import { OpenAPIV2 } from "openapi-types";
|
|
20
|
-
console.log("openapi-types imported");
|
|
21
|
-
|
|
22
|
-
import { Project } from "ts-morph";
|
|
23
|
-
console.log("ts-morph imported");
|
|
24
|
-
|
|
25
|
-
import { generateJsDoc } from "./utils/generateJsDoc.js";
|
|
26
|
-
|
|
27
|
-
const simplifyName = (str: string): string => str;
|
|
28
|
-
|
|
29
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
30
|
-
console.log("__dirname set:", __dirname);
|
|
31
|
-
|
|
32
|
-
const SPEC_FILE = join(__dirname, "output", "quickbase-fixed.json");
|
|
33
|
-
const OUTPUT_DIR = join(__dirname, "..", "src", "generated-unified");
|
|
34
|
-
const OUTPUT_FILE = join(OUTPUT_DIR, "QuickbaseClient.ts");
|
|
35
|
-
const MODELS_DIR = join(__dirname, "..", "src", "generated", "models");
|
|
36
|
-
|
|
37
|
-
function mapOpenApiTypeToTs(
|
|
38
|
-
openApiType: string | string[] | undefined
|
|
39
|
-
): string {
|
|
40
|
-
const type = Array.isArray(openApiType)
|
|
41
|
-
? openApiType[0]
|
|
42
|
-
: openApiType || "any";
|
|
43
|
-
switch (type.toLowerCase()) {
|
|
44
|
-
case "integer":
|
|
45
|
-
case "int":
|
|
46
|
-
case "number":
|
|
47
|
-
return "number";
|
|
48
|
-
case "string":
|
|
49
|
-
return "string";
|
|
50
|
-
case "boolean":
|
|
51
|
-
return "boolean";
|
|
52
|
-
default:
|
|
53
|
-
return "any";
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function mapRefToType(
|
|
58
|
-
schema: OpenAPIV2.SchemaObject | OpenAPIV2.ReferenceObject | undefined,
|
|
59
|
-
modelImports: Set<string>,
|
|
60
|
-
spec: OpenAPIV2.Document,
|
|
61
|
-
depth: number = 0,
|
|
62
|
-
availableModels: string[],
|
|
63
|
-
missingTypes: Set<string>
|
|
64
|
-
): string {
|
|
65
|
-
if (!schema) return "any";
|
|
66
|
-
|
|
67
|
-
if ("$ref" in schema && schema.$ref) {
|
|
68
|
-
const refParts = schema.$ref.split("/");
|
|
69
|
-
const model = refParts[refParts.length - 1];
|
|
70
|
-
const pascalModel = model.charAt(0).toUpperCase() + model.slice(1);
|
|
71
|
-
if (availableModels.includes(pascalModel)) {
|
|
72
|
-
modelImports.add(pascalModel);
|
|
73
|
-
return pascalModel;
|
|
74
|
-
}
|
|
75
|
-
missingTypes.add(pascalModel);
|
|
76
|
-
console.warn(`Type ${pascalModel} not found, defaulting to 'any'`);
|
|
77
|
-
return "any";
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if ("type" in schema) {
|
|
81
|
-
if (schema.type === "object" && schema.properties) {
|
|
82
|
-
const props = schema.properties;
|
|
83
|
-
const propTypes = Object.entries(props).map(([key, prop]) => {
|
|
84
|
-
const propSchema = prop as OpenAPIV2.SchemaObject;
|
|
85
|
-
const propType = mapRefToType(
|
|
86
|
-
propSchema,
|
|
87
|
-
modelImports,
|
|
88
|
-
spec,
|
|
89
|
-
depth + 1,
|
|
90
|
-
availableModels,
|
|
91
|
-
missingTypes
|
|
92
|
-
);
|
|
93
|
-
return `${key}${propSchema.required ? "" : "?"}: ${propType}`;
|
|
94
|
-
});
|
|
95
|
-
return `{ ${propTypes.join("; ")} }`;
|
|
96
|
-
}
|
|
97
|
-
if (schema.type === "array" && schema.items) {
|
|
98
|
-
const items = schema.items as
|
|
99
|
-
| OpenAPIV2.SchemaObject
|
|
100
|
-
| OpenAPIV2.ReferenceObject;
|
|
101
|
-
const itemType = mapRefToType(
|
|
102
|
-
items,
|
|
103
|
-
modelImports,
|
|
104
|
-
spec,
|
|
105
|
-
depth + 1,
|
|
106
|
-
availableModels,
|
|
107
|
-
missingTypes
|
|
108
|
-
);
|
|
109
|
-
return `${itemType}[]`;
|
|
110
|
-
}
|
|
111
|
-
return mapOpenApiTypeToTs(schema.type);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return "any";
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
interface PropertyDetail {
|
|
118
|
-
name: string;
|
|
119
|
-
type: string;
|
|
120
|
-
required: boolean;
|
|
121
|
-
jsdoc?: string;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function parseInterfaceProperties(
|
|
125
|
-
modelName: string,
|
|
126
|
-
modelsDir: string
|
|
127
|
-
): PropertyDetail[] {
|
|
128
|
-
const project = new Project();
|
|
129
|
-
const filePath = join(modelsDir, `${modelName}.ts`);
|
|
130
|
-
if (!existsSync(filePath)) {
|
|
131
|
-
console.warn(`Model file ${filePath} not found for ${modelName}`);
|
|
132
|
-
return [];
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const sourceFile = project.addSourceFileAtPath(filePath);
|
|
136
|
-
const interfaceDec = sourceFile.getInterface(modelName);
|
|
137
|
-
if (!interfaceDec) {
|
|
138
|
-
console.warn(`Interface ${modelName} not found in ${filePath}`);
|
|
139
|
-
return [];
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return interfaceDec.getProperties().map((prop) => {
|
|
143
|
-
const jsDocs = prop.getJsDocs();
|
|
144
|
-
const jsdocText =
|
|
145
|
-
jsDocs.length > 0 ? jsDocs[0].getDescription().trim() : undefined;
|
|
146
|
-
return {
|
|
147
|
-
name: prop.getName(),
|
|
148
|
-
type: prop.getType().getText(prop),
|
|
149
|
-
required: !prop.hasQuestionToken(),
|
|
150
|
-
jsdoc: jsdocText,
|
|
151
|
-
};
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
interface ParamDetail {
|
|
156
|
-
name: string;
|
|
157
|
-
type: string;
|
|
158
|
-
required: boolean;
|
|
159
|
-
properties: PropertyDetail[];
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function generateInterface(includeResponseProperties: boolean = false): void {
|
|
163
|
-
console.log(
|
|
164
|
-
"Generating interface with includeResponseProperties:",
|
|
165
|
-
includeResponseProperties
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
console.log("Checking spec file:", SPEC_FILE);
|
|
169
|
-
if (!existsSync(SPEC_FILE)) {
|
|
170
|
-
console.error(
|
|
171
|
-
`Spec file ${SPEC_FILE} not found. Run 'npm run fix-spec' first.`
|
|
172
|
-
);
|
|
173
|
-
process.exit(1);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
console.log("Reading spec file...");
|
|
177
|
-
const specContent = readFileSync(SPEC_FILE, "utf8");
|
|
178
|
-
const spec: OpenAPIV2.Document = JSON.parse(specContent);
|
|
179
|
-
console.log("Spec parsed, keys:", Object.keys(spec));
|
|
180
|
-
|
|
181
|
-
const { paths } = spec;
|
|
182
|
-
if (!paths) {
|
|
183
|
-
console.error("No 'paths' in spec");
|
|
184
|
-
process.exit(1);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
console.log("Checking models directory:", MODELS_DIR);
|
|
188
|
-
if (!existsSync(MODELS_DIR)) {
|
|
189
|
-
console.error(
|
|
190
|
-
`Models directory ${MODELS_DIR} not found. Run 'npm run gen:openapi' first.`
|
|
191
|
-
);
|
|
192
|
-
process.exit(1);
|
|
193
|
-
}
|
|
194
|
-
const availableModels = readdirSync(MODELS_DIR)
|
|
195
|
-
.filter((file) => file.endsWith(".ts") && !file.startsWith("index"))
|
|
196
|
-
.map((file) => file.replace(".ts", ""));
|
|
197
|
-
console.log("Available models:", availableModels);
|
|
198
|
-
|
|
199
|
-
const modelImports = new Set<string>();
|
|
200
|
-
const missingTypes = new Set<string>();
|
|
201
|
-
const methods: string[] = [];
|
|
202
|
-
|
|
203
|
-
for (const [path, methodsObj] of Object.entries(
|
|
204
|
-
paths as OpenAPIV2.PathsObject
|
|
205
|
-
)) {
|
|
206
|
-
if (!methodsObj) continue;
|
|
207
|
-
for (const [method, operation] of Object.entries(
|
|
208
|
-
methodsObj as OpenAPIV2.PathItemObject
|
|
209
|
-
)) {
|
|
210
|
-
const op = operation as OpenAPIV2.OperationObject | undefined;
|
|
211
|
-
if (!op || !op.operationId) continue;
|
|
212
|
-
|
|
213
|
-
const opId = simplifyName(op.operationId);
|
|
214
|
-
const summary = op.summary || "No description.";
|
|
215
|
-
const docLink = `https://developer.quickbase.com/operation/${op.operationId}`;
|
|
216
|
-
|
|
217
|
-
const paramDetails: ParamDetail[] = (op.parameters || [])
|
|
218
|
-
.filter((p: OpenAPIV2.ParameterObject | OpenAPIV2.ReferenceObject) => {
|
|
219
|
-
const param = p as OpenAPIV2.ParameterObject;
|
|
220
|
-
return !["QB-Realm-Hostname", "Authorization", "User-Agent"].includes(
|
|
221
|
-
param.name || ""
|
|
222
|
-
);
|
|
223
|
-
})
|
|
224
|
-
.map((param: OpenAPIV2.ParameterObject | OpenAPIV2.ReferenceObject) => {
|
|
225
|
-
const p = param as OpenAPIV2.ParameterObject;
|
|
226
|
-
if (!p.name) return null;
|
|
227
|
-
let type = "any";
|
|
228
|
-
let properties: PropertyDetail[] = [];
|
|
229
|
-
if ("schema" in p && p.schema) {
|
|
230
|
-
type = mapRefToType(
|
|
231
|
-
p.schema,
|
|
232
|
-
modelImports,
|
|
233
|
-
spec,
|
|
234
|
-
1,
|
|
235
|
-
availableModels,
|
|
236
|
-
missingTypes
|
|
237
|
-
);
|
|
238
|
-
if ("$ref" in p.schema && p.schema.$ref) {
|
|
239
|
-
const refParts = p.schema.$ref.split("/");
|
|
240
|
-
const model = refParts[refParts.length - 1];
|
|
241
|
-
const pascalModel =
|
|
242
|
-
model.charAt(0).toUpperCase() + model.slice(1);
|
|
243
|
-
properties = parseInterfaceProperties(pascalModel, MODELS_DIR);
|
|
244
|
-
}
|
|
245
|
-
} else if ("type" in p && p.type) {
|
|
246
|
-
type = mapOpenApiTypeToTs(p.type);
|
|
247
|
-
}
|
|
248
|
-
const paramName = p.in === "body" ? "body" : p.name;
|
|
249
|
-
return {
|
|
250
|
-
name: paramName,
|
|
251
|
-
type,
|
|
252
|
-
required: p.required || false,
|
|
253
|
-
properties,
|
|
254
|
-
};
|
|
255
|
-
})
|
|
256
|
-
.filter((p): p is ParamDetail => p !== null);
|
|
257
|
-
|
|
258
|
-
const params = paramDetails
|
|
259
|
-
.map((p) => `${p.name}${p.required ? "" : "?"}: ${p.type}`)
|
|
260
|
-
.join("; ");
|
|
261
|
-
|
|
262
|
-
const returnTypes = ["200", "207"]
|
|
263
|
-
.map(
|
|
264
|
-
(code) => op.responses?.[code] as OpenAPIV2.ResponseObject | undefined
|
|
265
|
-
)
|
|
266
|
-
.filter((response) => response?.schema)
|
|
267
|
-
.map((response) =>
|
|
268
|
-
mapRefToType(
|
|
269
|
-
response!.schema,
|
|
270
|
-
modelImports,
|
|
271
|
-
spec,
|
|
272
|
-
1,
|
|
273
|
-
availableModels,
|
|
274
|
-
missingTypes
|
|
275
|
-
)
|
|
276
|
-
);
|
|
277
|
-
const returnType =
|
|
278
|
-
returnTypes.length > 1
|
|
279
|
-
? returnTypes.join(" | ")
|
|
280
|
-
: returnTypes[0] || "void";
|
|
281
|
-
|
|
282
|
-
const returnTypeDetails: PropertyDetail[] =
|
|
283
|
-
includeResponseProperties &&
|
|
284
|
-
returnTypes.length === 1 &&
|
|
285
|
-
returnTypes[0] !== "void"
|
|
286
|
-
? parseInterfaceProperties(returnTypes[0], MODELS_DIR)
|
|
287
|
-
: [];
|
|
288
|
-
|
|
289
|
-
const jsDoc = generateJsDoc({
|
|
290
|
-
summary,
|
|
291
|
-
opId,
|
|
292
|
-
paramDetails,
|
|
293
|
-
returnType,
|
|
294
|
-
returnTypeDetails,
|
|
295
|
-
docLink,
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
methods.push(
|
|
299
|
-
`${jsDoc}\n ${opId}: (params: { ${params} }) => Promise<${returnType}>;`
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
console.log("Writing missing types report...");
|
|
305
|
-
writeFileSync(
|
|
306
|
-
join(OUTPUT_DIR, "missing-types-report.json"),
|
|
307
|
-
JSON.stringify({ missingTypes: Array.from(missingTypes) }, null, 2)
|
|
308
|
-
);
|
|
309
|
-
|
|
310
|
-
const importStatement =
|
|
311
|
-
modelImports.size > 0
|
|
312
|
-
? `import { ${Array.from(modelImports)
|
|
313
|
-
.sort()
|
|
314
|
-
.join(", ")} } from "../generated/models";`
|
|
315
|
-
: "";
|
|
316
|
-
const interfaceContent = `// Generated on ${new Date().toISOString()}\n${importStatement}\n\nexport interface QuickbaseClient {\n${methods.join(
|
|
317
|
-
"\n"
|
|
318
|
-
)}\n}\n`;
|
|
319
|
-
|
|
320
|
-
console.log("Ensuring output directory exists...");
|
|
321
|
-
if (!existsSync(OUTPUT_DIR)) {
|
|
322
|
-
mkdirSync(OUTPUT_DIR, { recursive: true });
|
|
323
|
-
}
|
|
324
|
-
writeFileSync(OUTPUT_FILE, interfaceContent, "utf8");
|
|
325
|
-
console.log("Generated:", OUTPUT_FILE);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
console.log("Entering try block");
|
|
329
|
-
try {
|
|
330
|
-
generateInterface(true); // Toggle response properties here
|
|
331
|
-
} catch (error) {
|
|
332
|
-
console.error("Error in generateInterface:", error);
|
|
333
|
-
process.exit(1);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
console.log("Script completed");
|