@boltic/sdk 0.0.1
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/LICENSE +21 -0
- package/README.md +1012 -0
- package/dist/databases/index.d.ts +1560 -0
- package/dist/databases/index.js +2 -0
- package/dist/databases/index.js.map +1 -0
- package/dist/databases/index.mjs +523 -0
- package/dist/databases/index.mjs.map +1 -0
- package/dist/databases/test-client-BM9X5DH9.mjs +4053 -0
- package/dist/databases/test-client-BM9X5DH9.mjs.map +1 -0
- package/dist/databases/test-client-D-SuKCUC.js +2 -0
- package/dist/databases/test-client-D-SuKCUC.js.map +1 -0
- package/dist/databases/testing.d.ts +1077 -0
- package/dist/databases/testing.js +2 -0
- package/dist/databases/testing.js.map +1 -0
- package/dist/databases/testing.mjs +139 -0
- package/dist/databases/testing.mjs.map +1 -0
- package/dist/package.json +15 -0
- package/dist/sdk.js +3972 -0
- package/dist/sdk.js.map +1 -0
- package/dist/sdk.mjs +3972 -0
- package/dist/sdk.mjs.map +1 -0
- package/dist/types/index.d.ts +1087 -0
- package/package.json +82 -0
package/dist/sdk.js
ADDED
|
@@ -0,0 +1,3972 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
function createErrorWithContext$1(message, context) {
|
|
4
|
+
const error = new Error(message);
|
|
5
|
+
if (context) {
|
|
6
|
+
error.context = context;
|
|
7
|
+
}
|
|
8
|
+
return error;
|
|
9
|
+
}
|
|
10
|
+
function isNetworkError(error) {
|
|
11
|
+
return error instanceof Error && (error.message.includes("network") || error.message.includes("fetch") || error.message.includes("timeout") || error.name === "AbortError");
|
|
12
|
+
}
|
|
13
|
+
function getHttpStatusCode$1(error) {
|
|
14
|
+
if (error && typeof error === "object") {
|
|
15
|
+
if ("response" in error && error.response && typeof error.response === "object") {
|
|
16
|
+
const response = error.response;
|
|
17
|
+
if ("status" in response && typeof response.status === "number") {
|
|
18
|
+
return response.status;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if ("status" in error && typeof error.status === "number") {
|
|
22
|
+
return error.status;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
function formatError$1(error) {
|
|
28
|
+
if (error instanceof Error) {
|
|
29
|
+
const context = error.context;
|
|
30
|
+
const statusCode = getHttpStatusCode$1(error);
|
|
31
|
+
let formatted = `${error.name}: ${error.message}`;
|
|
32
|
+
if (statusCode) {
|
|
33
|
+
formatted += ` (HTTP ${statusCode})`;
|
|
34
|
+
}
|
|
35
|
+
if (context) {
|
|
36
|
+
formatted += `
|
|
37
|
+
Context: ${JSON.stringify(context, null, 2)}`;
|
|
38
|
+
}
|
|
39
|
+
return formatted;
|
|
40
|
+
}
|
|
41
|
+
return String(error);
|
|
42
|
+
}
|
|
43
|
+
let AuthManager$1 = class AuthManager {
|
|
44
|
+
constructor(config) {
|
|
45
|
+
this.tokenInfo = null;
|
|
46
|
+
this.config = config;
|
|
47
|
+
this.validateApiKey(config.apiKey);
|
|
48
|
+
}
|
|
49
|
+
validateApiKey(apiKey) {
|
|
50
|
+
if (!apiKey || typeof apiKey !== "string" || apiKey.trim().length === 0) {
|
|
51
|
+
throw createErrorWithContext$1(
|
|
52
|
+
"API key is required and must be a non-empty string",
|
|
53
|
+
{
|
|
54
|
+
name: "AuthenticationError",
|
|
55
|
+
code: "INVALID_API_KEY"
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
if (apiKey.length < 10) {
|
|
60
|
+
throw createErrorWithContext$1(
|
|
61
|
+
"API key appears to be invalid (too short)",
|
|
62
|
+
{
|
|
63
|
+
name: "AuthenticationError",
|
|
64
|
+
code: "INVALID_API_KEY_FORMAT"
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
getAuthHeaders() {
|
|
70
|
+
return {
|
|
71
|
+
"x-boltic-token": this.config.apiKey
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
updateApiKey(newApiKey) {
|
|
75
|
+
this.validateApiKey(newApiKey);
|
|
76
|
+
this.config.apiKey = newApiKey;
|
|
77
|
+
this.tokenInfo = null;
|
|
78
|
+
}
|
|
79
|
+
isAuthenticated() {
|
|
80
|
+
return !!this.config.apiKey;
|
|
81
|
+
}
|
|
82
|
+
async validateApiKeyAsync() {
|
|
83
|
+
try {
|
|
84
|
+
this.validateApiKey(this.config.apiKey);
|
|
85
|
+
return true;
|
|
86
|
+
} catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
getTokenInfo() {
|
|
91
|
+
return this.tokenInfo ? { ...this.tokenInfo } : null;
|
|
92
|
+
}
|
|
93
|
+
// Generic method to get headers for any Boltic service
|
|
94
|
+
getServiceHeaders(service) {
|
|
95
|
+
const headers = this.getAuthHeaders();
|
|
96
|
+
if (service) {
|
|
97
|
+
headers["x-boltic-service"] = service;
|
|
98
|
+
}
|
|
99
|
+
return headers;
|
|
100
|
+
}
|
|
101
|
+
// Method to validate API key for specific service
|
|
102
|
+
async validateForService() {
|
|
103
|
+
try {
|
|
104
|
+
const isValid = await this.validateApiKeyAsync();
|
|
105
|
+
if (isValid) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
} catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
class ValidationError extends Error {
|
|
115
|
+
constructor(message, failures = []) {
|
|
116
|
+
super(message);
|
|
117
|
+
this.name = "ValidationError";
|
|
118
|
+
this.failures = failures;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
class ApiError extends Error {
|
|
122
|
+
constructor(message, statusCode, response) {
|
|
123
|
+
super(message);
|
|
124
|
+
this.name = "ApiError";
|
|
125
|
+
this.statusCode = statusCode;
|
|
126
|
+
this.response = response;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function createErrorWithContext(message, context) {
|
|
130
|
+
const error = new Error(message);
|
|
131
|
+
if (context) {
|
|
132
|
+
error.context = context;
|
|
133
|
+
}
|
|
134
|
+
return error;
|
|
135
|
+
}
|
|
136
|
+
function getHttpStatusCode(error) {
|
|
137
|
+
if (error && typeof error === "object") {
|
|
138
|
+
if ("response" in error && error.response && typeof error.response === "object") {
|
|
139
|
+
const response = error.response;
|
|
140
|
+
if ("status" in response && typeof response.status === "number") {
|
|
141
|
+
return response.status;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if ("status" in error && typeof error.status === "number") {
|
|
145
|
+
return error.status;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
function formatError(error) {
|
|
151
|
+
if (error instanceof Error) {
|
|
152
|
+
const context = error.context;
|
|
153
|
+
const statusCode = getHttpStatusCode(error);
|
|
154
|
+
let formatted = `${error.name}: ${error.message}`;
|
|
155
|
+
if (statusCode) {
|
|
156
|
+
formatted += ` (HTTP ${statusCode})`;
|
|
157
|
+
}
|
|
158
|
+
if (context) {
|
|
159
|
+
formatted += `
|
|
160
|
+
Context: ${JSON.stringify(context, null, 2)}`;
|
|
161
|
+
}
|
|
162
|
+
return formatted;
|
|
163
|
+
}
|
|
164
|
+
return String(error);
|
|
165
|
+
}
|
|
166
|
+
class AuthManager2 {
|
|
167
|
+
constructor(config) {
|
|
168
|
+
this.tokenInfo = null;
|
|
169
|
+
this.config = {
|
|
170
|
+
maxRetries: 3,
|
|
171
|
+
...config
|
|
172
|
+
};
|
|
173
|
+
this.validateApiKey(config.apiKey);
|
|
174
|
+
}
|
|
175
|
+
validateApiKey(apiKey) {
|
|
176
|
+
if (!apiKey || typeof apiKey !== "string" || apiKey.trim().length === 0) {
|
|
177
|
+
throw createErrorWithContext(
|
|
178
|
+
"API key is required and must be a non-empty string",
|
|
179
|
+
{
|
|
180
|
+
name: "AuthenticationError",
|
|
181
|
+
code: "INVALID_API_KEY"
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
if (apiKey.length < 10) {
|
|
186
|
+
throw createErrorWithContext(
|
|
187
|
+
"API key appears to be invalid (too short)",
|
|
188
|
+
{
|
|
189
|
+
name: "AuthenticationError",
|
|
190
|
+
code: "INVALID_API_KEY_FORMAT"
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
getAuthHeaders() {
|
|
196
|
+
return {
|
|
197
|
+
"x-boltic-token": this.config.apiKey
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
updateApiKey(newApiKey) {
|
|
201
|
+
this.validateApiKey(newApiKey);
|
|
202
|
+
this.config.apiKey = newApiKey;
|
|
203
|
+
this.tokenInfo = null;
|
|
204
|
+
}
|
|
205
|
+
isAuthenticated() {
|
|
206
|
+
return !!this.config.apiKey;
|
|
207
|
+
}
|
|
208
|
+
async validateApiKeyAsync() {
|
|
209
|
+
try {
|
|
210
|
+
this.validateApiKey(this.config.apiKey);
|
|
211
|
+
return true;
|
|
212
|
+
} catch {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
getTokenInfo() {
|
|
217
|
+
return this.tokenInfo ? { ...this.tokenInfo } : null;
|
|
218
|
+
}
|
|
219
|
+
getMaxRetries() {
|
|
220
|
+
return this.config.maxRetries || 3;
|
|
221
|
+
}
|
|
222
|
+
// Security methods to prevent API key exposure
|
|
223
|
+
toString() {
|
|
224
|
+
return `AuthManager { authenticated: ${this.isAuthenticated()}, maxRetries: ${this.getMaxRetries()} }`;
|
|
225
|
+
}
|
|
226
|
+
toJSON() {
|
|
227
|
+
return {
|
|
228
|
+
authenticated: this.isAuthenticated(),
|
|
229
|
+
maxRetries: this.getMaxRetries()
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
// Custom inspect method for Node.js console logging
|
|
233
|
+
[Symbol.for("nodejs.util.inspect.custom")]() {
|
|
234
|
+
return this.toString();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
class AxiosAdapter {
|
|
238
|
+
constructor() {
|
|
239
|
+
try {
|
|
240
|
+
this.axios = require("axios");
|
|
241
|
+
} catch (error) {
|
|
242
|
+
throw createErrorWithContext(
|
|
243
|
+
"Axios is required for Node.js < 18. Please install axios: npm install axios",
|
|
244
|
+
{ error }
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async request(config) {
|
|
249
|
+
try {
|
|
250
|
+
const axiosConfig = {
|
|
251
|
+
url: config.url,
|
|
252
|
+
method: config.method.toLowerCase(),
|
|
253
|
+
headers: config.headers,
|
|
254
|
+
params: config.params,
|
|
255
|
+
data: config.data,
|
|
256
|
+
timeout: config.timeout,
|
|
257
|
+
signal: config.signal,
|
|
258
|
+
validateStatus: () => true
|
|
259
|
+
// Don't throw on non-2xx status codes
|
|
260
|
+
};
|
|
261
|
+
const response = await this.axios(axiosConfig);
|
|
262
|
+
return {
|
|
263
|
+
data: response.data,
|
|
264
|
+
status: response.status,
|
|
265
|
+
statusText: response.statusText,
|
|
266
|
+
headers: response.headers || {}
|
|
267
|
+
};
|
|
268
|
+
} catch (error) {
|
|
269
|
+
const axiosError = error;
|
|
270
|
+
if (axiosError.code === "ECONNABORTED" || axiosError.message?.includes("timeout")) {
|
|
271
|
+
throw createErrorWithContext("Request timeout", {
|
|
272
|
+
url: config.url,
|
|
273
|
+
method: config.method,
|
|
274
|
+
timeout: config.timeout
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
if (axiosError.code === "ERR_NETWORK" || axiosError.code === "ENOTFOUND" || axiosError.code === "ECONNREFUSED" || axiosError.code === "EHOSTUNREACH" || axiosError.code === "ETIMEDOUT" || axiosError.code === "ERR_INTERNET_DISCONNECTED" || axiosError.message?.includes("network") || axiosError.message?.includes("internet") || axiosError.message?.includes("connection") || axiosError.message?.includes("resolve")) {
|
|
278
|
+
throw createErrorWithContext(
|
|
279
|
+
"Network connection failed. Please check your internet connection or VPN settings.",
|
|
280
|
+
{
|
|
281
|
+
url: config.url,
|
|
282
|
+
method: config.method,
|
|
283
|
+
networkError: true,
|
|
284
|
+
errorCode: axiosError.code,
|
|
285
|
+
originalMessage: axiosError.message
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
if (axiosError.name === "AbortError" || axiosError.code === "ERR_CANCELED") {
|
|
290
|
+
throw createErrorWithContext("Request was aborted", {
|
|
291
|
+
url: config.url,
|
|
292
|
+
method: config.method
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
throw createErrorWithContext(
|
|
296
|
+
`HTTP request failed: ${axiosError.message || "Unknown error"}`,
|
|
297
|
+
{
|
|
298
|
+
url: config.url,
|
|
299
|
+
method: config.method,
|
|
300
|
+
originalError: error
|
|
301
|
+
}
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
class FetchAdapter {
|
|
307
|
+
async request(config) {
|
|
308
|
+
const url = new URL(config.url);
|
|
309
|
+
if (config.params) {
|
|
310
|
+
Object.entries(config.params).forEach(([key, value]) => {
|
|
311
|
+
if (value !== void 0 && value !== null) {
|
|
312
|
+
url.searchParams.append(key, String(value));
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
const init = {
|
|
317
|
+
method: config.method,
|
|
318
|
+
headers: {
|
|
319
|
+
"Content-Type": "application/json",
|
|
320
|
+
...config.headers
|
|
321
|
+
},
|
|
322
|
+
signal: config.signal
|
|
323
|
+
};
|
|
324
|
+
if (config.data && ["POST", "PUT", "PATCH", "DELETE"].includes(config.method)) {
|
|
325
|
+
init.body = JSON.stringify(config.data);
|
|
326
|
+
}
|
|
327
|
+
try {
|
|
328
|
+
const controller = new AbortController();
|
|
329
|
+
let timeoutId;
|
|
330
|
+
if (config.timeout) {
|
|
331
|
+
timeoutId = setTimeout(() => controller.abort(), config.timeout);
|
|
332
|
+
init.signal = config.signal ? (() => {
|
|
333
|
+
const combinedController = new AbortController();
|
|
334
|
+
config.signal.addEventListener(
|
|
335
|
+
"abort",
|
|
336
|
+
() => combinedController.abort()
|
|
337
|
+
);
|
|
338
|
+
controller.signal.addEventListener(
|
|
339
|
+
"abort",
|
|
340
|
+
() => combinedController.abort()
|
|
341
|
+
);
|
|
342
|
+
return combinedController.signal;
|
|
343
|
+
})() : controller.signal;
|
|
344
|
+
}
|
|
345
|
+
const response = await fetch(url.toString(), init);
|
|
346
|
+
if (timeoutId) {
|
|
347
|
+
clearTimeout(timeoutId);
|
|
348
|
+
}
|
|
349
|
+
const contentType = response.headers.get("content-type");
|
|
350
|
+
let data;
|
|
351
|
+
if (contentType?.includes("application/json")) {
|
|
352
|
+
data = await response.json();
|
|
353
|
+
} else {
|
|
354
|
+
data = await response.text();
|
|
355
|
+
}
|
|
356
|
+
const headers = {};
|
|
357
|
+
response.headers.forEach((value, key) => {
|
|
358
|
+
headers[key] = value;
|
|
359
|
+
});
|
|
360
|
+
const httpResponse = {
|
|
361
|
+
data,
|
|
362
|
+
status: response.status,
|
|
363
|
+
statusText: response.statusText,
|
|
364
|
+
headers
|
|
365
|
+
};
|
|
366
|
+
return httpResponse;
|
|
367
|
+
} catch (error) {
|
|
368
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
369
|
+
throw createErrorWithContext("Request was aborted", {
|
|
370
|
+
type: "AbortError",
|
|
371
|
+
url: config.url,
|
|
372
|
+
method: config.method
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
if (error instanceof Error) {
|
|
376
|
+
const errorMessage = error.message.toLowerCase();
|
|
377
|
+
if (error.name === "TypeError" && (errorMessage.includes("network") || errorMessage.includes("fetch") || errorMessage.includes("failed to fetch") || errorMessage.includes("internet") || errorMessage.includes("connection") || errorMessage.includes("resolve") || errorMessage.includes("unreachable"))) {
|
|
378
|
+
throw createErrorWithContext(
|
|
379
|
+
"Network connection failed. Please check your internet connection or VPN settings.",
|
|
380
|
+
{
|
|
381
|
+
url: config.url,
|
|
382
|
+
method: config.method,
|
|
383
|
+
networkError: true,
|
|
384
|
+
originalMessage: error.message
|
|
385
|
+
}
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
throw createErrorWithContext(
|
|
390
|
+
`HTTP request failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
391
|
+
{
|
|
392
|
+
url: config.url,
|
|
393
|
+
method: config.method,
|
|
394
|
+
originalError: error
|
|
395
|
+
}
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
function createHttpAdapter() {
|
|
401
|
+
if (typeof fetch !== "undefined") {
|
|
402
|
+
return new FetchAdapter();
|
|
403
|
+
}
|
|
404
|
+
try {
|
|
405
|
+
return new AxiosAdapter();
|
|
406
|
+
} catch (error) {
|
|
407
|
+
throw createErrorWithContext(
|
|
408
|
+
"No suitable HTTP adapter found. Please use Node.js >= 18 or install axios: npm install axios",
|
|
409
|
+
{ error }
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
class InterceptorManagerImpl {
|
|
414
|
+
constructor() {
|
|
415
|
+
this.requestInterceptors = /* @__PURE__ */ new Map();
|
|
416
|
+
this.responseInterceptors = /* @__PURE__ */ new Map();
|
|
417
|
+
this.nextId = 0;
|
|
418
|
+
this.request = {
|
|
419
|
+
use: (interceptor) => {
|
|
420
|
+
const id = this.nextId++;
|
|
421
|
+
this.requestInterceptors.set(id, interceptor);
|
|
422
|
+
return id;
|
|
423
|
+
},
|
|
424
|
+
eject: (id) => {
|
|
425
|
+
this.requestInterceptors.delete(id);
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
this.response = {
|
|
429
|
+
use: (onFulfilled, onRejected) => {
|
|
430
|
+
const id = this.nextId++;
|
|
431
|
+
this.responseInterceptors.set(id, {
|
|
432
|
+
fulfilled: onFulfilled,
|
|
433
|
+
rejected: onRejected
|
|
434
|
+
});
|
|
435
|
+
return id;
|
|
436
|
+
},
|
|
437
|
+
eject: (id) => {
|
|
438
|
+
this.responseInterceptors.delete(id);
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
async executeRequestInterceptors(config) {
|
|
443
|
+
let result = config;
|
|
444
|
+
for (const interceptor of this.requestInterceptors.values()) {
|
|
445
|
+
result = await interceptor(result);
|
|
446
|
+
}
|
|
447
|
+
return result;
|
|
448
|
+
}
|
|
449
|
+
async executeResponseInterceptors(response) {
|
|
450
|
+
let result = response;
|
|
451
|
+
for (const { fulfilled } of this.responseInterceptors.values()) {
|
|
452
|
+
if (fulfilled) {
|
|
453
|
+
result = await fulfilled(result);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return result;
|
|
457
|
+
}
|
|
458
|
+
async executeErrorInterceptors(error) {
|
|
459
|
+
let result = error;
|
|
460
|
+
for (const { rejected } of this.responseInterceptors.values()) {
|
|
461
|
+
if (rejected) {
|
|
462
|
+
result = await rejected(result);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return result;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
class BaseClient {
|
|
469
|
+
constructor(config, authManager) {
|
|
470
|
+
this.config = config;
|
|
471
|
+
this.authManager = authManager;
|
|
472
|
+
this.httpAdapter = createHttpAdapter();
|
|
473
|
+
this.interceptors = new InterceptorManagerImpl();
|
|
474
|
+
this.setupDefaultInterceptors();
|
|
475
|
+
}
|
|
476
|
+
setupDefaultInterceptors() {
|
|
477
|
+
this.interceptors.request.use((config) => {
|
|
478
|
+
const authHeaders = this.authManager.getAuthHeaders();
|
|
479
|
+
config.headers = {
|
|
480
|
+
...config.headers,
|
|
481
|
+
...authHeaders,
|
|
482
|
+
...this.config.headers
|
|
483
|
+
};
|
|
484
|
+
return config;
|
|
485
|
+
});
|
|
486
|
+
this.interceptors.response.use(
|
|
487
|
+
(response) => {
|
|
488
|
+
if (this.config.debug) {
|
|
489
|
+
console.log("HTTP Response:", response);
|
|
490
|
+
}
|
|
491
|
+
return response;
|
|
492
|
+
},
|
|
493
|
+
(error) => {
|
|
494
|
+
return this.handleError(error);
|
|
495
|
+
}
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
handleError(error) {
|
|
499
|
+
if (this.config.debug) {
|
|
500
|
+
console.error("HTTP Error:", error);
|
|
501
|
+
}
|
|
502
|
+
if (error instanceof Error && error.context) {
|
|
503
|
+
throw error;
|
|
504
|
+
}
|
|
505
|
+
const statusCode = getHttpStatusCode(error);
|
|
506
|
+
if (!statusCode) {
|
|
507
|
+
throw createErrorWithContext("Network request failed", {
|
|
508
|
+
name: "NetworkError",
|
|
509
|
+
originalError: error
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
const errorData = error.response?.data || error.data;
|
|
513
|
+
const message = errorData?.message || errorData?.error || `HTTP ${statusCode} error`;
|
|
514
|
+
throw createErrorWithContext(message, {
|
|
515
|
+
name: "ApiError",
|
|
516
|
+
statusCode,
|
|
517
|
+
response: errorData,
|
|
518
|
+
isClientError: statusCode >= 400 && statusCode < 500,
|
|
519
|
+
isServerError: statusCode >= 500,
|
|
520
|
+
isAuthError: statusCode === 401 || statusCode === 403,
|
|
521
|
+
isNotFoundError: statusCode === 404,
|
|
522
|
+
isRateLimitError: statusCode === 429
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
async request(config) {
|
|
526
|
+
let lastError;
|
|
527
|
+
const maxRetries = this.config.maxRetries;
|
|
528
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
529
|
+
try {
|
|
530
|
+
if (!config.url.startsWith("http")) {
|
|
531
|
+
config.url = `${this.config.baseURL}${config.url}`;
|
|
532
|
+
}
|
|
533
|
+
if (!config.timeout) {
|
|
534
|
+
config.timeout = this.config.timeout;
|
|
535
|
+
}
|
|
536
|
+
const requestConfig = await this.interceptors.executeRequestInterceptors(config);
|
|
537
|
+
const response = await this.httpAdapter.request(requestConfig);
|
|
538
|
+
if (response.status >= 400) {
|
|
539
|
+
const error = createErrorWithContext(
|
|
540
|
+
`HTTP ${response.status} error`,
|
|
541
|
+
{
|
|
542
|
+
name: "ApiError",
|
|
543
|
+
statusCode: response.status,
|
|
544
|
+
response: response.data,
|
|
545
|
+
statusText: response.statusText
|
|
546
|
+
}
|
|
547
|
+
);
|
|
548
|
+
throw await this.interceptors.executeErrorInterceptors(error);
|
|
549
|
+
}
|
|
550
|
+
return await this.interceptors.executeResponseInterceptors(
|
|
551
|
+
response
|
|
552
|
+
);
|
|
553
|
+
} catch (error) {
|
|
554
|
+
lastError = error;
|
|
555
|
+
if (attempt === maxRetries) {
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
const statusCode = getHttpStatusCode(error);
|
|
559
|
+
if (statusCode && statusCode >= 400 && statusCode < 500) {
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
if (attempt < maxRetries) {
|
|
563
|
+
const delay = this.config.retryDelay * Math.pow(2, attempt);
|
|
564
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
throw await this.interceptors.executeErrorInterceptors(lastError);
|
|
569
|
+
}
|
|
570
|
+
get(url, config) {
|
|
571
|
+
return this.request({ ...config, method: "GET", url });
|
|
572
|
+
}
|
|
573
|
+
post(url, data, config) {
|
|
574
|
+
return this.request({ ...config, method: "POST", url, data });
|
|
575
|
+
}
|
|
576
|
+
put(url, data, config) {
|
|
577
|
+
return this.request({ ...config, method: "PUT", url, data });
|
|
578
|
+
}
|
|
579
|
+
patch(url, data, config) {
|
|
580
|
+
return this.request({ ...config, method: "PATCH", url, data });
|
|
581
|
+
}
|
|
582
|
+
delete(url, config) {
|
|
583
|
+
return this.request({ ...config, method: "DELETE", url });
|
|
584
|
+
}
|
|
585
|
+
getInterceptors() {
|
|
586
|
+
return this.interceptors;
|
|
587
|
+
}
|
|
588
|
+
updateConfig(updates) {
|
|
589
|
+
this.config = { ...this.config, ...updates };
|
|
590
|
+
}
|
|
591
|
+
getConfig() {
|
|
592
|
+
return { ...this.config };
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
const REGION_CONFIGS = {
|
|
596
|
+
"asia-south1": {
|
|
597
|
+
local: {
|
|
598
|
+
baseURL: "http://localhost:8000",
|
|
599
|
+
timeout: 3e4,
|
|
600
|
+
debug: true
|
|
601
|
+
},
|
|
602
|
+
sit: {
|
|
603
|
+
baseURL: "https://asia-south1.api.fcz0.de/service/sdk/boltic-tables",
|
|
604
|
+
timeout: 15e3
|
|
605
|
+
},
|
|
606
|
+
uat: {
|
|
607
|
+
baseURL: "https://asia-south1.api.uat.fcz0.de/service/sdk/boltic-tables",
|
|
608
|
+
timeout: 15e3
|
|
609
|
+
},
|
|
610
|
+
prod: {
|
|
611
|
+
baseURL: "https://asia-south1.api.boltic.io/service/sdk/boltic-tables",
|
|
612
|
+
timeout: 1e4
|
|
613
|
+
}
|
|
614
|
+
},
|
|
615
|
+
"us-central1": {
|
|
616
|
+
local: {
|
|
617
|
+
baseURL: "http://localhost:8000",
|
|
618
|
+
timeout: 3e4,
|
|
619
|
+
debug: true
|
|
620
|
+
},
|
|
621
|
+
sit: {
|
|
622
|
+
baseURL: "https://us-central1.api.fcz0.de/service/sdk/boltic-tables",
|
|
623
|
+
timeout: 15e3
|
|
624
|
+
},
|
|
625
|
+
uat: {
|
|
626
|
+
baseURL: "https://us-central1.api.uat.fcz0.de/service/sdk/boltic-tables",
|
|
627
|
+
timeout: 15e3
|
|
628
|
+
},
|
|
629
|
+
prod: {
|
|
630
|
+
baseURL: "https://us-central1.api.boltic.io/service/sdk/boltic-tables",
|
|
631
|
+
timeout: 1e4
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
class ConfigManager {
|
|
636
|
+
constructor(apiKey, environment = "prod", region = "asia-south1", overrides) {
|
|
637
|
+
const envConfig = REGION_CONFIGS[region][environment];
|
|
638
|
+
this.config = {
|
|
639
|
+
apiKey,
|
|
640
|
+
environment,
|
|
641
|
+
region,
|
|
642
|
+
retryAttempts: 3,
|
|
643
|
+
retryDelay: 1e3,
|
|
644
|
+
maxRetries: 3,
|
|
645
|
+
debug: false,
|
|
646
|
+
headers: {},
|
|
647
|
+
...envConfig,
|
|
648
|
+
...overrides
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
getConfig() {
|
|
652
|
+
return { ...this.config };
|
|
653
|
+
}
|
|
654
|
+
updateConfig(updates) {
|
|
655
|
+
this.config = { ...this.config, ...updates };
|
|
656
|
+
}
|
|
657
|
+
// Security methods to prevent API key exposure
|
|
658
|
+
toString() {
|
|
659
|
+
return `ConfigManager { environment: "${this.config.environment}", region: "${this.config.region}", debug: ${this.config.debug} }`;
|
|
660
|
+
}
|
|
661
|
+
toJSON() {
|
|
662
|
+
const safeConfig = { ...this.config };
|
|
663
|
+
delete safeConfig.apiKey;
|
|
664
|
+
return safeConfig;
|
|
665
|
+
}
|
|
666
|
+
// Custom inspect method for Node.js console logging
|
|
667
|
+
[Symbol.for("nodejs.util.inspect.custom")]() {
|
|
668
|
+
return this.toString();
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
function filterObjectFields(obj, fields) {
|
|
672
|
+
if (!fields || fields.length === 0) {
|
|
673
|
+
return obj;
|
|
674
|
+
}
|
|
675
|
+
const filtered = {};
|
|
676
|
+
for (const field of fields) {
|
|
677
|
+
if (field in obj) {
|
|
678
|
+
filtered[field] = obj[field];
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
return filtered;
|
|
682
|
+
}
|
|
683
|
+
function filterArrayFields(arr, fields) {
|
|
684
|
+
if (!fields || fields.length === 0) {
|
|
685
|
+
return arr;
|
|
686
|
+
}
|
|
687
|
+
return arr.map((obj) => filterObjectFields(obj, fields));
|
|
688
|
+
}
|
|
689
|
+
const COLUMN_ENDPOINTS = {
|
|
690
|
+
list: {
|
|
691
|
+
path: "/tables/{table_id}/fields/list",
|
|
692
|
+
method: "POST",
|
|
693
|
+
authenticated: true,
|
|
694
|
+
rateLimit: { requests: 200, window: 6e4 }
|
|
695
|
+
},
|
|
696
|
+
create: {
|
|
697
|
+
path: "/tables/{table_id}/fields",
|
|
698
|
+
method: "POST",
|
|
699
|
+
authenticated: true
|
|
700
|
+
},
|
|
701
|
+
get: {
|
|
702
|
+
path: "/tables/{table_id}/fields/{field_id}",
|
|
703
|
+
method: "GET",
|
|
704
|
+
authenticated: true,
|
|
705
|
+
rateLimit: { requests: 300, window: 6e4 }
|
|
706
|
+
},
|
|
707
|
+
update: {
|
|
708
|
+
path: "/tables/{table_id}/fields/{field_id}",
|
|
709
|
+
method: "PATCH",
|
|
710
|
+
authenticated: true
|
|
711
|
+
},
|
|
712
|
+
delete: {
|
|
713
|
+
path: "/tables/{table_id}/fields/{field_id}",
|
|
714
|
+
method: "DELETE",
|
|
715
|
+
authenticated: true
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
const buildEndpointPath$1 = (endpoint, params = {}) => {
|
|
719
|
+
let path = endpoint.path;
|
|
720
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
721
|
+
path = path.replace(`{${key}}`, encodeURIComponent(value));
|
|
722
|
+
});
|
|
723
|
+
const unreplacedParams = path.match(/\{([^}]+)\}/g);
|
|
724
|
+
if (unreplacedParams) {
|
|
725
|
+
throw new Error(`Missing path parameters: ${unreplacedParams.join(", ")}`);
|
|
726
|
+
}
|
|
727
|
+
return path;
|
|
728
|
+
};
|
|
729
|
+
const DateFormatEnum = Object.freeze({
|
|
730
|
+
MMDDYY: "%m/%d/%y",
|
|
731
|
+
MMDDYYYY: "%m/%d/%Y",
|
|
732
|
+
MM_DD_YYYY: "%m-%d-%Y",
|
|
733
|
+
DD_MM_YYYY: "%d-%m-%Y",
|
|
734
|
+
DDMMYYYY: "%d/%m/%Y",
|
|
735
|
+
DDMMYY: "%d/%m/%y",
|
|
736
|
+
YYYY_MM_DD: "%Y-%m-%d",
|
|
737
|
+
MMMM__DD__YYYY: "%B %d %Y",
|
|
738
|
+
MMM__DD__YYYY: "%b %d %Y",
|
|
739
|
+
ddd__MMM__DD__YYYY: "%a %b %d %Y"
|
|
740
|
+
});
|
|
741
|
+
const TimeFormatEnum = Object.freeze({
|
|
742
|
+
HH_mm_ss: "%H:%M:%S",
|
|
743
|
+
HH_mm_ssZ: "%H:%M:%SZ",
|
|
744
|
+
HH_mm_ss_SSS: "%H:%M:%S.%f",
|
|
745
|
+
HH_mm_ss__Z: "%H:%M:%S %Z",
|
|
746
|
+
HH_mm__AMPM: "%I:%M %p",
|
|
747
|
+
// 12-hour format with AM/PM
|
|
748
|
+
HH_mm_ss__AMPM: "%I:%M:%S %p"
|
|
749
|
+
});
|
|
750
|
+
function transformColumnCreateRequest(request) {
|
|
751
|
+
if (!request || typeof request !== "object") {
|
|
752
|
+
throw new Error("Invalid request: single column data is required");
|
|
753
|
+
}
|
|
754
|
+
if (!request.name || !request.type) {
|
|
755
|
+
throw new Error("Column name and type are required");
|
|
756
|
+
}
|
|
757
|
+
return transformFieldDefinition$1(request);
|
|
758
|
+
}
|
|
759
|
+
function transformFieldDefinition$1(field) {
|
|
760
|
+
return {
|
|
761
|
+
name: field.name,
|
|
762
|
+
type: field.type,
|
|
763
|
+
is_nullable: field.is_nullable ?? true,
|
|
764
|
+
is_primary_key: field.is_primary_key ?? false,
|
|
765
|
+
is_unique: field.is_unique ?? false,
|
|
766
|
+
is_visible: field.is_visible ?? true,
|
|
767
|
+
is_readonly: field.is_readonly ?? false,
|
|
768
|
+
is_indexed: field.is_indexed ?? false,
|
|
769
|
+
field_order: field.field_order ?? 1,
|
|
770
|
+
alignment: field.alignment ?? "left",
|
|
771
|
+
timezone: field.timezone ?? void 0,
|
|
772
|
+
date_format: field.date_format ? transformDateFormat(field.date_format) : void 0,
|
|
773
|
+
time_format: field.time_format ? transformTimeFormat(field.time_format) : void 0,
|
|
774
|
+
decimals: field.decimals ?? void 0,
|
|
775
|
+
currency_format: field.currency_format ?? void 0,
|
|
776
|
+
selection_source: field.type === "dropdown" && !field.selection_source ? "provide-static-list" : field.selection_source ?? void 0,
|
|
777
|
+
selectable_items: field.selectable_items ?? void 0,
|
|
778
|
+
multiple_selections: field.multiple_selections ?? void 0,
|
|
779
|
+
phone_format: field.phone_format ?? void 0,
|
|
780
|
+
vector_dimension: field.vector_dimension ?? void 0,
|
|
781
|
+
description: field.description ?? void 0,
|
|
782
|
+
default_value: field.default_value ?? void 0
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
function transformColumnUpdateRequest(updates) {
|
|
786
|
+
const apiRequest = {};
|
|
787
|
+
if (updates.name !== void 0) apiRequest.name = updates.name;
|
|
788
|
+
if (updates.type !== void 0) apiRequest.type = updates.type;
|
|
789
|
+
if (updates.description !== void 0)
|
|
790
|
+
apiRequest.description = updates.description;
|
|
791
|
+
if (updates.is_nullable !== void 0)
|
|
792
|
+
apiRequest.is_nullable = updates.is_nullable;
|
|
793
|
+
if (updates.is_unique !== void 0) apiRequest.is_unique = updates.is_unique;
|
|
794
|
+
if (updates.is_primary_key !== void 0)
|
|
795
|
+
apiRequest.is_primary_key = updates.is_primary_key;
|
|
796
|
+
if (updates.is_indexed !== void 0)
|
|
797
|
+
apiRequest.is_indexed = updates.is_indexed;
|
|
798
|
+
if (updates.is_visible !== void 0)
|
|
799
|
+
apiRequest.is_visible = updates.is_visible;
|
|
800
|
+
if (updates.is_readonly !== void 0)
|
|
801
|
+
apiRequest.is_readonly = updates.is_readonly;
|
|
802
|
+
if (updates.default_value !== void 0)
|
|
803
|
+
apiRequest.default_value = updates.default_value;
|
|
804
|
+
if (updates.field_order !== void 0)
|
|
805
|
+
apiRequest.field_order = updates.field_order;
|
|
806
|
+
if (updates.alignment !== void 0) apiRequest.alignment = updates.alignment;
|
|
807
|
+
if (updates.decimals !== void 0) apiRequest.decimals = updates.decimals;
|
|
808
|
+
if (updates.currency_format !== void 0)
|
|
809
|
+
apiRequest.currency_format = updates.currency_format;
|
|
810
|
+
if (updates.type === "dropdown") {
|
|
811
|
+
apiRequest.selection_source = "provide-static-list";
|
|
812
|
+
} else if (updates.selectable_items !== void 0) {
|
|
813
|
+
apiRequest.selection_source = "provide-static-list";
|
|
814
|
+
} else if (updates.selection_source !== void 0) {
|
|
815
|
+
apiRequest.selection_source = updates.selection_source;
|
|
816
|
+
}
|
|
817
|
+
if (updates.selectable_items !== void 0)
|
|
818
|
+
apiRequest.selectable_items = updates.selectable_items;
|
|
819
|
+
if (updates.multiple_selections !== void 0)
|
|
820
|
+
apiRequest.multiple_selections = updates.multiple_selections;
|
|
821
|
+
if (updates.phone_format !== void 0)
|
|
822
|
+
apiRequest.phone_format = updates.phone_format;
|
|
823
|
+
if (updates.timezone !== void 0) apiRequest.timezone = updates.timezone;
|
|
824
|
+
if (updates.vector_dimension !== void 0)
|
|
825
|
+
apiRequest.vector_dimension = updates.vector_dimension;
|
|
826
|
+
if (updates.date_format !== void 0) {
|
|
827
|
+
apiRequest.date_format = transformDateFormat(updates.date_format);
|
|
828
|
+
}
|
|
829
|
+
if (updates.time_format !== void 0) {
|
|
830
|
+
apiRequest.time_format = transformTimeFormat(updates.time_format);
|
|
831
|
+
}
|
|
832
|
+
return apiRequest;
|
|
833
|
+
}
|
|
834
|
+
function transformDateFormat(dateFormat) {
|
|
835
|
+
return DateFormatEnum[dateFormat] || dateFormat;
|
|
836
|
+
}
|
|
837
|
+
function transformTimeFormat(timeFormat) {
|
|
838
|
+
return TimeFormatEnum[timeFormat] || timeFormat;
|
|
839
|
+
}
|
|
840
|
+
class ColumnsApiClient {
|
|
841
|
+
constructor(apiKey, config = {}) {
|
|
842
|
+
this.config = { apiKey, ...config };
|
|
843
|
+
this.httpAdapter = createHttpAdapter();
|
|
844
|
+
const environment = config.environment || "prod";
|
|
845
|
+
const region = config.region || "asia-south1";
|
|
846
|
+
this.baseURL = this.getBaseURL(environment, region);
|
|
847
|
+
}
|
|
848
|
+
getBaseURL(environment, region) {
|
|
849
|
+
const regionConfig = REGION_CONFIGS[region];
|
|
850
|
+
if (!regionConfig) {
|
|
851
|
+
throw new Error(`Unsupported region: ${region}`);
|
|
852
|
+
}
|
|
853
|
+
const envConfig = regionConfig[environment];
|
|
854
|
+
if (!envConfig) {
|
|
855
|
+
throw new Error(
|
|
856
|
+
`Unsupported environment: ${environment} for region: ${region}`
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
return `${envConfig.baseURL}/v1`;
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Create a single column in a table
|
|
863
|
+
*/
|
|
864
|
+
async createColumn(tableId, request) {
|
|
865
|
+
try {
|
|
866
|
+
const endpoint = COLUMN_ENDPOINTS.create;
|
|
867
|
+
const url = `${this.baseURL}${buildEndpointPath$1(endpoint, { table_id: tableId })}`;
|
|
868
|
+
const transformedRequest = transformColumnCreateRequest(request);
|
|
869
|
+
const response = await this.httpAdapter.request({
|
|
870
|
+
url,
|
|
871
|
+
method: endpoint.method,
|
|
872
|
+
headers: this.buildHeaders(),
|
|
873
|
+
data: transformedRequest,
|
|
874
|
+
timeout: this.config.timeout
|
|
875
|
+
});
|
|
876
|
+
if (this.config.debug) {
|
|
877
|
+
console.log(
|
|
878
|
+
"Column API Response:",
|
|
879
|
+
JSON.stringify(response.data, null, 2)
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
return response.data;
|
|
883
|
+
} catch (error) {
|
|
884
|
+
return this.formatErrorResponse(error);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Create multiple columns in a table (one by one)
|
|
889
|
+
*/
|
|
890
|
+
async createColumns(tableId, request) {
|
|
891
|
+
try {
|
|
892
|
+
const columns = request.columns;
|
|
893
|
+
const createdColumns = [];
|
|
894
|
+
for (const column of columns) {
|
|
895
|
+
const result = await this.createColumn(tableId, column);
|
|
896
|
+
if ("error" in result) {
|
|
897
|
+
return result;
|
|
898
|
+
}
|
|
899
|
+
createdColumns.push(result.data);
|
|
900
|
+
}
|
|
901
|
+
if (request.fields && createdColumns.length > 0) {
|
|
902
|
+
const filteredColumns = filterArrayFields(
|
|
903
|
+
createdColumns,
|
|
904
|
+
request.fields
|
|
905
|
+
);
|
|
906
|
+
createdColumns.splice(0, createdColumns.length, ...filteredColumns);
|
|
907
|
+
}
|
|
908
|
+
return {
|
|
909
|
+
data: createdColumns,
|
|
910
|
+
message: "Columns created successfully"
|
|
911
|
+
};
|
|
912
|
+
} catch (error) {
|
|
913
|
+
return this.formatErrorResponse(error);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* List columns in a table with filtering and pagination
|
|
918
|
+
*/
|
|
919
|
+
async listColumns(tableId, options = {}) {
|
|
920
|
+
try {
|
|
921
|
+
const endpoint = COLUMN_ENDPOINTS.list;
|
|
922
|
+
const url = `${this.baseURL}${buildEndpointPath$1(endpoint, { table_id: tableId })}?no_cache=true`;
|
|
923
|
+
const response = await this.httpAdapter.request({
|
|
924
|
+
url,
|
|
925
|
+
method: endpoint.method,
|
|
926
|
+
headers: this.buildHeaders(),
|
|
927
|
+
data: options,
|
|
928
|
+
timeout: this.config.timeout
|
|
929
|
+
});
|
|
930
|
+
const responseData = response.data;
|
|
931
|
+
if (options.fields && responseData.data) {
|
|
932
|
+
responseData.data = filterArrayFields(
|
|
933
|
+
responseData.data,
|
|
934
|
+
options.fields
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
return responseData;
|
|
938
|
+
} catch (error) {
|
|
939
|
+
return this.formatErrorResponse(error);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Get a single column by ID
|
|
944
|
+
*/
|
|
945
|
+
async getColumn(tableId, columnId, options = {}) {
|
|
946
|
+
try {
|
|
947
|
+
const endpoint = COLUMN_ENDPOINTS.get;
|
|
948
|
+
const url = `${this.baseURL}${buildEndpointPath$1(endpoint, {
|
|
949
|
+
table_id: tableId,
|
|
950
|
+
field_id: columnId
|
|
951
|
+
})}`;
|
|
952
|
+
const response = await this.httpAdapter.request({
|
|
953
|
+
url,
|
|
954
|
+
method: endpoint.method,
|
|
955
|
+
headers: this.buildHeaders(),
|
|
956
|
+
timeout: this.config.timeout
|
|
957
|
+
});
|
|
958
|
+
if (this.config.debug) {
|
|
959
|
+
console.log(
|
|
960
|
+
"Column API Response:",
|
|
961
|
+
JSON.stringify(response.data, null, 2)
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
const responseData = response.data;
|
|
965
|
+
if (options.fields && responseData.data) {
|
|
966
|
+
responseData.data = filterObjectFields(
|
|
967
|
+
responseData.data,
|
|
968
|
+
options.fields
|
|
969
|
+
);
|
|
970
|
+
}
|
|
971
|
+
return responseData;
|
|
972
|
+
} catch (error) {
|
|
973
|
+
return this.formatErrorResponse(error);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* Update a column
|
|
978
|
+
*/
|
|
979
|
+
async updateColumn(tableId, columnId, updates) {
|
|
980
|
+
try {
|
|
981
|
+
const endpoint = COLUMN_ENDPOINTS.update;
|
|
982
|
+
const url = `${this.baseURL}${buildEndpointPath$1(endpoint, {
|
|
983
|
+
table_id: tableId,
|
|
984
|
+
field_id: columnId
|
|
985
|
+
})}`;
|
|
986
|
+
const transformedUpdates = transformColumnUpdateRequest(updates);
|
|
987
|
+
const response = await this.httpAdapter.request({
|
|
988
|
+
url,
|
|
989
|
+
method: endpoint.method,
|
|
990
|
+
headers: this.buildHeaders(),
|
|
991
|
+
data: transformedUpdates,
|
|
992
|
+
timeout: this.config.timeout
|
|
993
|
+
});
|
|
994
|
+
const responseData = response.data;
|
|
995
|
+
if (updates.fields && responseData.data) {
|
|
996
|
+
responseData.data = filterObjectFields(
|
|
997
|
+
responseData.data,
|
|
998
|
+
updates.fields
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
return responseData;
|
|
1002
|
+
} catch (error) {
|
|
1003
|
+
return this.formatErrorResponse(error);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Delete a column
|
|
1008
|
+
*/
|
|
1009
|
+
async deleteColumn(tableId, columnId) {
|
|
1010
|
+
try {
|
|
1011
|
+
const endpoint = COLUMN_ENDPOINTS.delete;
|
|
1012
|
+
const url = `${this.baseURL}${buildEndpointPath$1(endpoint, {
|
|
1013
|
+
table_id: tableId,
|
|
1014
|
+
field_id: columnId
|
|
1015
|
+
})}`;
|
|
1016
|
+
const response = await this.httpAdapter.request({
|
|
1017
|
+
url,
|
|
1018
|
+
method: endpoint.method,
|
|
1019
|
+
headers: this.buildHeaders(),
|
|
1020
|
+
timeout: this.config.timeout
|
|
1021
|
+
});
|
|
1022
|
+
return response.data;
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
return this.formatErrorResponse(error);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Find column by name in a table
|
|
1029
|
+
*/
|
|
1030
|
+
async findColumnByName(tableId, columnName) {
|
|
1031
|
+
try {
|
|
1032
|
+
const apiRequest = {
|
|
1033
|
+
page: { page_no: 1, page_size: 1 },
|
|
1034
|
+
filters: [
|
|
1035
|
+
{
|
|
1036
|
+
field: "name",
|
|
1037
|
+
operator: "=",
|
|
1038
|
+
values: [columnName]
|
|
1039
|
+
}
|
|
1040
|
+
],
|
|
1041
|
+
sort: []
|
|
1042
|
+
};
|
|
1043
|
+
const listResult = await this.listColumns(
|
|
1044
|
+
tableId,
|
|
1045
|
+
apiRequest
|
|
1046
|
+
);
|
|
1047
|
+
if ("error" in listResult) {
|
|
1048
|
+
return listResult;
|
|
1049
|
+
}
|
|
1050
|
+
const column = listResult.data[0] || null;
|
|
1051
|
+
return {
|
|
1052
|
+
data: column,
|
|
1053
|
+
message: column ? "Column found" : "Column not found"
|
|
1054
|
+
};
|
|
1055
|
+
} catch (error) {
|
|
1056
|
+
return this.formatErrorResponse(error);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1060
|
+
* Helper function to convert ColumnDetails to ColumnUpdateRequest format
|
|
1061
|
+
*/
|
|
1062
|
+
convertColumnDetailsToUpdateRequest(columnDetails) {
|
|
1063
|
+
return {
|
|
1064
|
+
name: columnDetails.name,
|
|
1065
|
+
type: columnDetails.type,
|
|
1066
|
+
description: columnDetails.description,
|
|
1067
|
+
is_nullable: columnDetails.is_nullable,
|
|
1068
|
+
is_unique: columnDetails.is_unique,
|
|
1069
|
+
is_indexed: columnDetails.is_indexed,
|
|
1070
|
+
is_visible: columnDetails.is_visible,
|
|
1071
|
+
is_primary_key: columnDetails.is_primary_key,
|
|
1072
|
+
is_readonly: columnDetails.is_readonly,
|
|
1073
|
+
default_value: columnDetails.default_value,
|
|
1074
|
+
field_order: columnDetails.field_order,
|
|
1075
|
+
alignment: columnDetails.alignment,
|
|
1076
|
+
decimals: columnDetails.decimals,
|
|
1077
|
+
currency_format: columnDetails.currency_format,
|
|
1078
|
+
selection_source: columnDetails.selection_source,
|
|
1079
|
+
selectable_items: columnDetails.selectable_items,
|
|
1080
|
+
multiple_selections: columnDetails.multiple_selections,
|
|
1081
|
+
phone_format: columnDetails.phone_format,
|
|
1082
|
+
date_format: columnDetails.date_format,
|
|
1083
|
+
time_format: columnDetails.time_format,
|
|
1084
|
+
timezone: columnDetails.timezone,
|
|
1085
|
+
vector_dimension: columnDetails.vector_dimension
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Update a column by name
|
|
1090
|
+
*/
|
|
1091
|
+
async updateColumnByName(tableId, columnName, updates) {
|
|
1092
|
+
try {
|
|
1093
|
+
const findResult = await this.findColumnByName(tableId, columnName);
|
|
1094
|
+
if ("error" in findResult) {
|
|
1095
|
+
return findResult;
|
|
1096
|
+
}
|
|
1097
|
+
if (!findResult.data) {
|
|
1098
|
+
return {
|
|
1099
|
+
error: {
|
|
1100
|
+
code: "COLUMN_NOT_FOUND",
|
|
1101
|
+
message: `Column '${columnName}' not found in table`,
|
|
1102
|
+
meta: ["404"]
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
const existingColumnAsUpdate = this.convertColumnDetailsToUpdateRequest(
|
|
1107
|
+
findResult.data
|
|
1108
|
+
);
|
|
1109
|
+
const mergedUpdates = {
|
|
1110
|
+
...existingColumnAsUpdate,
|
|
1111
|
+
...updates
|
|
1112
|
+
};
|
|
1113
|
+
return await this.updateColumn(
|
|
1114
|
+
tableId,
|
|
1115
|
+
findResult.data.id,
|
|
1116
|
+
mergedUpdates
|
|
1117
|
+
);
|
|
1118
|
+
} catch (error) {
|
|
1119
|
+
return this.formatErrorResponse(error);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Delete column by name
|
|
1124
|
+
*/
|
|
1125
|
+
async deleteColumnByName(tableId, columnName) {
|
|
1126
|
+
try {
|
|
1127
|
+
const findResult = await this.findColumnByName(tableId, columnName);
|
|
1128
|
+
if ("error" in findResult) {
|
|
1129
|
+
return findResult;
|
|
1130
|
+
}
|
|
1131
|
+
if (!findResult.data) {
|
|
1132
|
+
return {
|
|
1133
|
+
error: {
|
|
1134
|
+
code: "COLUMN_NOT_FOUND",
|
|
1135
|
+
message: `Column '${columnName}' not found in table`,
|
|
1136
|
+
meta: ["Column not found"]
|
|
1137
|
+
}
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
return await this.deleteColumn(tableId, findResult.data.id);
|
|
1141
|
+
} catch (error) {
|
|
1142
|
+
return this.formatErrorResponse(error);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
buildHeaders() {
|
|
1146
|
+
return {
|
|
1147
|
+
"Content-Type": "application/json",
|
|
1148
|
+
Accept: "application/json",
|
|
1149
|
+
"x-boltic-token": this.config.apiKey
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
formatErrorResponse(error) {
|
|
1153
|
+
if (this.config.debug) {
|
|
1154
|
+
console.error("Columns API Error:", error);
|
|
1155
|
+
}
|
|
1156
|
+
if (error && typeof error === "object" && "response" in error) {
|
|
1157
|
+
const apiError = error;
|
|
1158
|
+
if (apiError.response?.data?.error) {
|
|
1159
|
+
return apiError.response.data;
|
|
1160
|
+
}
|
|
1161
|
+
return {
|
|
1162
|
+
error: {
|
|
1163
|
+
code: "API_ERROR",
|
|
1164
|
+
message: error.message || "Unknown API error",
|
|
1165
|
+
meta: [`Status: ${apiError.response?.status || "unknown"}`]
|
|
1166
|
+
}
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
if (error && typeof error === "object" && "message" in error) {
|
|
1170
|
+
return {
|
|
1171
|
+
error: {
|
|
1172
|
+
code: "CLIENT_ERROR",
|
|
1173
|
+
message: error.message,
|
|
1174
|
+
meta: ["Client-side error occurred"]
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
return {
|
|
1179
|
+
error: {
|
|
1180
|
+
code: "UNKNOWN_ERROR",
|
|
1181
|
+
message: "An unexpected error occurred",
|
|
1182
|
+
meta: ["Unknown error type"]
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
const TABLE_ENDPOINTS = {
|
|
1188
|
+
list: {
|
|
1189
|
+
path: "/tables/list",
|
|
1190
|
+
method: "POST",
|
|
1191
|
+
authenticated: true,
|
|
1192
|
+
rateLimit: { requests: 200, window: 6e4 }
|
|
1193
|
+
},
|
|
1194
|
+
create: {
|
|
1195
|
+
path: "/tables",
|
|
1196
|
+
method: "POST",
|
|
1197
|
+
authenticated: true
|
|
1198
|
+
},
|
|
1199
|
+
get: {
|
|
1200
|
+
path: "/tables/{table_id}",
|
|
1201
|
+
method: "GET",
|
|
1202
|
+
authenticated: true,
|
|
1203
|
+
rateLimit: { requests: 300, window: 6e4 }
|
|
1204
|
+
},
|
|
1205
|
+
update: {
|
|
1206
|
+
path: "/tables/{table_id}",
|
|
1207
|
+
method: "PATCH",
|
|
1208
|
+
authenticated: true
|
|
1209
|
+
},
|
|
1210
|
+
delete: {
|
|
1211
|
+
path: "/tables/{table_id}",
|
|
1212
|
+
method: "DELETE",
|
|
1213
|
+
authenticated: true
|
|
1214
|
+
}
|
|
1215
|
+
};
|
|
1216
|
+
const buildEndpointPath = (endpoint, params = {}) => {
|
|
1217
|
+
let path = endpoint.path;
|
|
1218
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
1219
|
+
path = path.replace(`{${key}}`, encodeURIComponent(value));
|
|
1220
|
+
});
|
|
1221
|
+
const unreplacedParams = path.match(/\{([^}]+)\}/g);
|
|
1222
|
+
if (unreplacedParams) {
|
|
1223
|
+
throw new Error(`Missing path parameters: ${unreplacedParams.join(", ")}`);
|
|
1224
|
+
}
|
|
1225
|
+
return path;
|
|
1226
|
+
};
|
|
1227
|
+
const FILTER_OPERATORS = {
|
|
1228
|
+
// Relational operators
|
|
1229
|
+
EQUALS: "=",
|
|
1230
|
+
NOT_EQUALS: "!=",
|
|
1231
|
+
GREATER_THAN: ">",
|
|
1232
|
+
GREATER_THAN_EQUAL: ">=",
|
|
1233
|
+
LESS_THAN: "<",
|
|
1234
|
+
LESS_THAN_EQUAL: "<=",
|
|
1235
|
+
// String operators
|
|
1236
|
+
LIKE: "LIKE",
|
|
1237
|
+
// contains (case-sensitive)
|
|
1238
|
+
ILIKE: "ILIKE",
|
|
1239
|
+
// contains (case-insensitive)
|
|
1240
|
+
STARTS_WITH: "STARTS WITH",
|
|
1241
|
+
// Array/Set operators
|
|
1242
|
+
IN: "IN",
|
|
1243
|
+
// is one of
|
|
1244
|
+
NOT_IN: "NOT IN",
|
|
1245
|
+
// is not one of
|
|
1246
|
+
// Special operators
|
|
1247
|
+
IS_EMPTY: "IS EMPTY",
|
|
1248
|
+
IS_NULL: "IS NULL",
|
|
1249
|
+
IS_NOT_NULL: "IS NOT NULL",
|
|
1250
|
+
BETWEEN: "BETWEEN",
|
|
1251
|
+
// Dropdown/Array specific operators
|
|
1252
|
+
ARRAY_CONTAINS: "@>",
|
|
1253
|
+
// is exactly for dropdown
|
|
1254
|
+
ARRAY_NOT_CONTAINS: "NOT @>",
|
|
1255
|
+
// is different from for dropdown
|
|
1256
|
+
ANY: "ANY",
|
|
1257
|
+
// contains (case-sensitive) for dropdown
|
|
1258
|
+
IS_ONE_OF_ARRAY: "IS ONE OF",
|
|
1259
|
+
// is one of for dropdown
|
|
1260
|
+
DROPDOWN_ITEM_STARTS_WITH: "DROPDOWN ITEM STARTS WITH",
|
|
1261
|
+
// Date operators
|
|
1262
|
+
WITHIN: "WITHIN"
|
|
1263
|
+
// date range operator
|
|
1264
|
+
};
|
|
1265
|
+
const OPERATOR_MAPPING = {
|
|
1266
|
+
// Basic comparisons
|
|
1267
|
+
$eq: FILTER_OPERATORS.EQUALS,
|
|
1268
|
+
$ne: FILTER_OPERATORS.NOT_EQUALS,
|
|
1269
|
+
$gt: FILTER_OPERATORS.GREATER_THAN,
|
|
1270
|
+
$gte: FILTER_OPERATORS.GREATER_THAN_EQUAL,
|
|
1271
|
+
$lt: FILTER_OPERATORS.LESS_THAN,
|
|
1272
|
+
$lte: FILTER_OPERATORS.LESS_THAN_EQUAL,
|
|
1273
|
+
// String operations
|
|
1274
|
+
$like: FILTER_OPERATORS.LIKE,
|
|
1275
|
+
$ilike: FILTER_OPERATORS.ILIKE,
|
|
1276
|
+
$startsWith: FILTER_OPERATORS.STARTS_WITH,
|
|
1277
|
+
// Array operations
|
|
1278
|
+
$in: FILTER_OPERATORS.IN,
|
|
1279
|
+
$notIn: FILTER_OPERATORS.NOT_IN,
|
|
1280
|
+
// Special operations
|
|
1281
|
+
$between: FILTER_OPERATORS.BETWEEN,
|
|
1282
|
+
$isEmpty: FILTER_OPERATORS.IS_EMPTY,
|
|
1283
|
+
$isNull: FILTER_OPERATORS.IS_NULL,
|
|
1284
|
+
$isNotNull: FILTER_OPERATORS.IS_NOT_NULL,
|
|
1285
|
+
// Array/dropdown operations
|
|
1286
|
+
$arrayContains: FILTER_OPERATORS.ARRAY_CONTAINS,
|
|
1287
|
+
$arrayNotContains: FILTER_OPERATORS.ARRAY_NOT_CONTAINS,
|
|
1288
|
+
$any: FILTER_OPERATORS.ANY,
|
|
1289
|
+
$isOneOfArray: FILTER_OPERATORS.IS_ONE_OF_ARRAY,
|
|
1290
|
+
$dropdownItemStartsWith: FILTER_OPERATORS.DROPDOWN_ITEM_STARTS_WITH,
|
|
1291
|
+
// Date operations
|
|
1292
|
+
$within: FILTER_OPERATORS.WITHIN
|
|
1293
|
+
};
|
|
1294
|
+
function mapWhereToFilters(where) {
|
|
1295
|
+
const filters = [];
|
|
1296
|
+
Object.entries(where).forEach(([field, condition]) => {
|
|
1297
|
+
if (typeof condition !== "object" || Array.isArray(condition) || condition === null) {
|
|
1298
|
+
filters.push({
|
|
1299
|
+
field,
|
|
1300
|
+
operator: FILTER_OPERATORS.EQUALS,
|
|
1301
|
+
values: [condition]
|
|
1302
|
+
});
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
Object.entries(condition).forEach(([operator, value]) => {
|
|
1306
|
+
const apiOperator = OPERATOR_MAPPING[operator];
|
|
1307
|
+
if (!apiOperator) {
|
|
1308
|
+
throw new Error(`Unsupported operator: ${operator}`);
|
|
1309
|
+
}
|
|
1310
|
+
let values;
|
|
1311
|
+
if (apiOperator === FILTER_OPERATORS.BETWEEN && Array.isArray(value) && value.length === 2) {
|
|
1312
|
+
values = value;
|
|
1313
|
+
} else if ((apiOperator === FILTER_OPERATORS.IN || apiOperator === FILTER_OPERATORS.NOT_IN || apiOperator === FILTER_OPERATORS.IS_ONE_OF_ARRAY) && Array.isArray(value)) {
|
|
1314
|
+
values = value;
|
|
1315
|
+
} else if (apiOperator === FILTER_OPERATORS.IS_NULL || apiOperator === FILTER_OPERATORS.IS_NOT_NULL || apiOperator === FILTER_OPERATORS.IS_EMPTY) {
|
|
1316
|
+
values = [];
|
|
1317
|
+
} else {
|
|
1318
|
+
values = [value];
|
|
1319
|
+
}
|
|
1320
|
+
filters.push({
|
|
1321
|
+
field,
|
|
1322
|
+
operator: apiOperator,
|
|
1323
|
+
values
|
|
1324
|
+
});
|
|
1325
|
+
});
|
|
1326
|
+
});
|
|
1327
|
+
return filters;
|
|
1328
|
+
}
|
|
1329
|
+
function transformTableCreateRequest(request, options = {}) {
|
|
1330
|
+
return {
|
|
1331
|
+
name: request.name,
|
|
1332
|
+
description: request.description,
|
|
1333
|
+
fields: request.fields.map(transformFieldDefinition),
|
|
1334
|
+
is_ai_generated_schema: options.is_ai_generated_schema || false,
|
|
1335
|
+
is_template: options.is_template || false
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
function transformFieldDefinition(field) {
|
|
1339
|
+
return {
|
|
1340
|
+
name: field.name,
|
|
1341
|
+
type: field.type,
|
|
1342
|
+
is_nullable: field.is_nullable ?? true,
|
|
1343
|
+
is_primary_key: field.is_primary_key ?? false,
|
|
1344
|
+
is_unique: field.is_unique ?? false,
|
|
1345
|
+
is_indexed: field.is_indexed ?? false,
|
|
1346
|
+
is_visible: field.is_visible ?? true,
|
|
1347
|
+
is_readonly: field.is_readonly ?? false,
|
|
1348
|
+
field_order: field.field_order ?? 1,
|
|
1349
|
+
alignment: field.alignment ?? "left",
|
|
1350
|
+
timezone: field.timezone ?? void 0,
|
|
1351
|
+
date_format: field.date_format ?? void 0,
|
|
1352
|
+
time_format: field.time_format ?? void 0,
|
|
1353
|
+
decimals: field.decimals ?? void 0,
|
|
1354
|
+
currency_format: field.currency_format ?? void 0,
|
|
1355
|
+
selection_source: field.type === "dropdown" && !field.selection_source ? "provide-static-list" : field.selection_source ?? void 0,
|
|
1356
|
+
selectable_items: field.selectable_items ?? void 0,
|
|
1357
|
+
multiple_selections: field.multiple_selections ?? false,
|
|
1358
|
+
phone_format: field.phone_format ?? void 0,
|
|
1359
|
+
vector_dimension: field.vector_dimension ?? void 0,
|
|
1360
|
+
description: field.description,
|
|
1361
|
+
default_value: field.default_value
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
class TablesApiClient {
|
|
1365
|
+
constructor(apiKey, config = {}) {
|
|
1366
|
+
this.config = { apiKey, ...config };
|
|
1367
|
+
this.httpAdapter = createHttpAdapter();
|
|
1368
|
+
const environment = config.environment || "prod";
|
|
1369
|
+
const region = config.region || "asia-south1";
|
|
1370
|
+
this.baseURL = this.getBaseURL(environment, region);
|
|
1371
|
+
}
|
|
1372
|
+
getBaseURL(environment, region) {
|
|
1373
|
+
const regionConfig = REGION_CONFIGS[region];
|
|
1374
|
+
if (!regionConfig) {
|
|
1375
|
+
throw new Error(`Unsupported region: ${region}`);
|
|
1376
|
+
}
|
|
1377
|
+
const envConfig = regionConfig[environment];
|
|
1378
|
+
if (!envConfig) {
|
|
1379
|
+
throw new Error(
|
|
1380
|
+
`Unsupported environment: ${environment} for region: ${region}`
|
|
1381
|
+
);
|
|
1382
|
+
}
|
|
1383
|
+
return `${envConfig.baseURL}/v1`;
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Create a new table
|
|
1387
|
+
*/
|
|
1388
|
+
async createTable(request, options = {}) {
|
|
1389
|
+
try {
|
|
1390
|
+
const endpoint = TABLE_ENDPOINTS.create;
|
|
1391
|
+
const url = `${this.baseURL}${endpoint.path}`;
|
|
1392
|
+
const transformedRequest = transformTableCreateRequest(request, options);
|
|
1393
|
+
const response = await this.httpAdapter.request({
|
|
1394
|
+
url,
|
|
1395
|
+
method: endpoint.method,
|
|
1396
|
+
headers: this.buildHeaders(),
|
|
1397
|
+
data: transformedRequest,
|
|
1398
|
+
timeout: this.config.timeout
|
|
1399
|
+
});
|
|
1400
|
+
return response.data;
|
|
1401
|
+
} catch (error) {
|
|
1402
|
+
return this.formatErrorResponse(error);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
/**
|
|
1406
|
+
* List tables with filtering and pagination
|
|
1407
|
+
*/
|
|
1408
|
+
async listTables(options = {}) {
|
|
1409
|
+
try {
|
|
1410
|
+
const endpoint = TABLE_ENDPOINTS.list;
|
|
1411
|
+
const url = `${this.baseURL}${endpoint.path}`;
|
|
1412
|
+
const response = await this.httpAdapter.request({
|
|
1413
|
+
url,
|
|
1414
|
+
method: endpoint.method,
|
|
1415
|
+
headers: this.buildHeaders(),
|
|
1416
|
+
data: options,
|
|
1417
|
+
timeout: this.config.timeout
|
|
1418
|
+
});
|
|
1419
|
+
const responseData = response.data;
|
|
1420
|
+
if (options.fields && responseData.data) {
|
|
1421
|
+
responseData.data = filterArrayFields(
|
|
1422
|
+
responseData.data,
|
|
1423
|
+
options.fields
|
|
1424
|
+
);
|
|
1425
|
+
}
|
|
1426
|
+
return responseData;
|
|
1427
|
+
} catch (error) {
|
|
1428
|
+
return this.formatErrorResponse(error);
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
/**
|
|
1432
|
+
* Get a specific table by ID
|
|
1433
|
+
*/
|
|
1434
|
+
async getTable(tableId, options = {}) {
|
|
1435
|
+
try {
|
|
1436
|
+
const endpoint = TABLE_ENDPOINTS.get;
|
|
1437
|
+
const url = `${this.baseURL}${buildEndpointPath(endpoint, { table_id: tableId })}`;
|
|
1438
|
+
const response = await this.httpAdapter.request({
|
|
1439
|
+
url,
|
|
1440
|
+
method: endpoint.method,
|
|
1441
|
+
headers: this.buildHeaders(),
|
|
1442
|
+
timeout: this.config.timeout
|
|
1443
|
+
});
|
|
1444
|
+
const responseData = response.data;
|
|
1445
|
+
if (options.fields && responseData.data) {
|
|
1446
|
+
responseData.data = filterObjectFields(
|
|
1447
|
+
responseData.data,
|
|
1448
|
+
options.fields
|
|
1449
|
+
);
|
|
1450
|
+
}
|
|
1451
|
+
return responseData;
|
|
1452
|
+
} catch (error) {
|
|
1453
|
+
return this.formatErrorResponse(error);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
/**
|
|
1457
|
+
* Update an existing table
|
|
1458
|
+
*/
|
|
1459
|
+
async updateTable(tableId, updates) {
|
|
1460
|
+
try {
|
|
1461
|
+
const { fields, ...updateData } = updates;
|
|
1462
|
+
const endpoint = TABLE_ENDPOINTS.update;
|
|
1463
|
+
const url = `${this.baseURL}${buildEndpointPath(endpoint, { table_id: tableId })}`;
|
|
1464
|
+
const response = await this.httpAdapter.request({
|
|
1465
|
+
url,
|
|
1466
|
+
method: endpoint.method,
|
|
1467
|
+
headers: this.buildHeaders(),
|
|
1468
|
+
data: updateData,
|
|
1469
|
+
timeout: this.config.timeout
|
|
1470
|
+
});
|
|
1471
|
+
const responseData = response.data;
|
|
1472
|
+
if (fields && responseData.data) {
|
|
1473
|
+
responseData.data = filterObjectFields(
|
|
1474
|
+
responseData.data,
|
|
1475
|
+
fields
|
|
1476
|
+
);
|
|
1477
|
+
}
|
|
1478
|
+
return responseData;
|
|
1479
|
+
} catch (error) {
|
|
1480
|
+
return this.formatErrorResponse(error);
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
/**
|
|
1484
|
+
* Delete a table
|
|
1485
|
+
*/
|
|
1486
|
+
async deleteTable(tableId) {
|
|
1487
|
+
try {
|
|
1488
|
+
const endpoint = TABLE_ENDPOINTS.delete;
|
|
1489
|
+
const url = `${this.baseURL}${buildEndpointPath(endpoint, { table_id: tableId })}`;
|
|
1490
|
+
const response = await this.httpAdapter.request({
|
|
1491
|
+
url,
|
|
1492
|
+
method: endpoint.method,
|
|
1493
|
+
headers: this.buildHeaders(),
|
|
1494
|
+
timeout: this.config.timeout
|
|
1495
|
+
});
|
|
1496
|
+
return response.data;
|
|
1497
|
+
} catch (error) {
|
|
1498
|
+
return this.formatErrorResponse(error);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
// Private helper methods
|
|
1502
|
+
buildHeaders() {
|
|
1503
|
+
return {
|
|
1504
|
+
"Content-Type": "application/json",
|
|
1505
|
+
Accept: "application/json",
|
|
1506
|
+
"x-boltic-token": this.config.apiKey
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
formatErrorResponse(error) {
|
|
1510
|
+
if (this.config.debug) {
|
|
1511
|
+
console.error("Tables API Error:", error);
|
|
1512
|
+
}
|
|
1513
|
+
if (error && typeof error === "object" && "response" in error) {
|
|
1514
|
+
const apiError = error;
|
|
1515
|
+
if (apiError.response?.data?.error) {
|
|
1516
|
+
return apiError.response.data;
|
|
1517
|
+
}
|
|
1518
|
+
return {
|
|
1519
|
+
error: {
|
|
1520
|
+
code: "API_ERROR",
|
|
1521
|
+
message: error.message || "Unknown API error",
|
|
1522
|
+
meta: [`Status: ${apiError.response?.status || "unknown"}`]
|
|
1523
|
+
}
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
1526
|
+
if (error && typeof error === "object" && "message" in error) {
|
|
1527
|
+
return {
|
|
1528
|
+
error: {
|
|
1529
|
+
code: "CLIENT_ERROR",
|
|
1530
|
+
message: error.message,
|
|
1531
|
+
meta: ["Client-side error occurred"]
|
|
1532
|
+
}
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1535
|
+
return {
|
|
1536
|
+
error: {
|
|
1537
|
+
code: "UNKNOWN_ERROR",
|
|
1538
|
+
message: "An unexpected error occurred",
|
|
1539
|
+
meta: ["Unknown error type"]
|
|
1540
|
+
}
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
function isErrorResponse(response) {
|
|
1545
|
+
return "error" in response && response.error !== void 0;
|
|
1546
|
+
}
|
|
1547
|
+
function isListResponse(response) {
|
|
1548
|
+
return "pagination" in response;
|
|
1549
|
+
}
|
|
1550
|
+
class BaseResource {
|
|
1551
|
+
constructor(client, basePath) {
|
|
1552
|
+
this.client = client;
|
|
1553
|
+
this.basePath = basePath;
|
|
1554
|
+
}
|
|
1555
|
+
// Public getter for basePath
|
|
1556
|
+
getBasePath() {
|
|
1557
|
+
return this.basePath;
|
|
1558
|
+
}
|
|
1559
|
+
async makeRequest(method, path, data, options) {
|
|
1560
|
+
const url = `${this.basePath}${path}`;
|
|
1561
|
+
try {
|
|
1562
|
+
let response;
|
|
1563
|
+
switch (method) {
|
|
1564
|
+
case "GET":
|
|
1565
|
+
response = await this.client.get(url, {
|
|
1566
|
+
params: options?.params
|
|
1567
|
+
});
|
|
1568
|
+
break;
|
|
1569
|
+
case "POST":
|
|
1570
|
+
response = await this.client.post(url, data, {
|
|
1571
|
+
params: options?.params
|
|
1572
|
+
});
|
|
1573
|
+
break;
|
|
1574
|
+
case "PUT":
|
|
1575
|
+
response = await this.client.put(url, data, {
|
|
1576
|
+
params: options?.params
|
|
1577
|
+
});
|
|
1578
|
+
break;
|
|
1579
|
+
case "PATCH":
|
|
1580
|
+
response = await this.client.patch(url, data, {
|
|
1581
|
+
params: options?.params
|
|
1582
|
+
});
|
|
1583
|
+
break;
|
|
1584
|
+
case "DELETE":
|
|
1585
|
+
response = await this.client.delete(url, {
|
|
1586
|
+
params: options?.params
|
|
1587
|
+
});
|
|
1588
|
+
break;
|
|
1589
|
+
}
|
|
1590
|
+
return response.data;
|
|
1591
|
+
} catch (error) {
|
|
1592
|
+
return {
|
|
1593
|
+
error: {
|
|
1594
|
+
code: "CLIENT_ERROR",
|
|
1595
|
+
message: formatError(error),
|
|
1596
|
+
meta: ["Request failed"]
|
|
1597
|
+
}
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
buildQueryParams(options = {}) {
|
|
1602
|
+
const params = {};
|
|
1603
|
+
if (options.fields?.length) {
|
|
1604
|
+
params.fields = options.fields.join(",");
|
|
1605
|
+
}
|
|
1606
|
+
if (options.sort?.length) {
|
|
1607
|
+
params.sort = options.sort.map((s) => `${s.field}:${s.order}`).join(",");
|
|
1608
|
+
}
|
|
1609
|
+
if (options.limit !== void 0) {
|
|
1610
|
+
params.limit = options.limit;
|
|
1611
|
+
}
|
|
1612
|
+
if (options.offset !== void 0) {
|
|
1613
|
+
params.offset = options.offset;
|
|
1614
|
+
}
|
|
1615
|
+
if (options.where) {
|
|
1616
|
+
Object.entries(options.where).forEach(([key, value]) => {
|
|
1617
|
+
if (value !== void 0 && value !== null) {
|
|
1618
|
+
params[`where[${key}]`] = typeof value === "object" ? JSON.stringify(value) : value;
|
|
1619
|
+
}
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
return params;
|
|
1623
|
+
}
|
|
1624
|
+
handleResponse(response) {
|
|
1625
|
+
if ("error" in response) {
|
|
1626
|
+
if (this.client.getConfig().debug) {
|
|
1627
|
+
console.error("API Error:", response.error);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
return response;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
class TableResource extends BaseResource {
|
|
1634
|
+
constructor(client) {
|
|
1635
|
+
super(client, "/v1/tables");
|
|
1636
|
+
const config = client.getConfig();
|
|
1637
|
+
this.tablesApiClient = new TablesApiClient(config.apiKey, {
|
|
1638
|
+
environment: config.environment,
|
|
1639
|
+
timeout: config.timeout,
|
|
1640
|
+
debug: config.debug,
|
|
1641
|
+
retryAttempts: config.retryAttempts,
|
|
1642
|
+
retryDelay: config.retryDelay,
|
|
1643
|
+
headers: config.headers
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
/**
|
|
1647
|
+
* Create a new table
|
|
1648
|
+
*/
|
|
1649
|
+
async create(data) {
|
|
1650
|
+
try {
|
|
1651
|
+
const processedData = { ...data };
|
|
1652
|
+
if (data.fields && data.fields.length > 0) {
|
|
1653
|
+
processedData.fields = await this.processFieldsDefaults(data.fields);
|
|
1654
|
+
}
|
|
1655
|
+
const result = await this.tablesApiClient.createTable(processedData);
|
|
1656
|
+
if (isErrorResponse(result)) {
|
|
1657
|
+
throw new ApiError(
|
|
1658
|
+
result.error.message || "Create table failed",
|
|
1659
|
+
400,
|
|
1660
|
+
result.error
|
|
1661
|
+
);
|
|
1662
|
+
}
|
|
1663
|
+
return result;
|
|
1664
|
+
} catch (error) {
|
|
1665
|
+
throw error instanceof ApiError ? error : new ApiError(this.formatError(error), 500);
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Process fields with defaults for table creation
|
|
1670
|
+
*/
|
|
1671
|
+
async processFieldsDefaults(fields) {
|
|
1672
|
+
const processedFields = [];
|
|
1673
|
+
for (let i = 0; i < fields.length; i++) {
|
|
1674
|
+
const field = fields[i];
|
|
1675
|
+
const processedField = { ...field };
|
|
1676
|
+
if (processedField.is_primary_key === void 0) {
|
|
1677
|
+
processedField.is_primary_key = false;
|
|
1678
|
+
}
|
|
1679
|
+
if (processedField.is_unique === void 0) {
|
|
1680
|
+
processedField.is_unique = false;
|
|
1681
|
+
}
|
|
1682
|
+
if (processedField.is_nullable === void 0) {
|
|
1683
|
+
processedField.is_nullable = true;
|
|
1684
|
+
}
|
|
1685
|
+
if (processedField.is_indexed === void 0) {
|
|
1686
|
+
processedField.is_indexed = false;
|
|
1687
|
+
}
|
|
1688
|
+
if (processedField.field_order === void 0) {
|
|
1689
|
+
processedField.field_order = i + 1;
|
|
1690
|
+
}
|
|
1691
|
+
if (processedField.field_order <= 0 || processedField.field_order >= 2147483647) {
|
|
1692
|
+
throw new Error(
|
|
1693
|
+
"Field order must be a number greater than 0 and less than 2147483647"
|
|
1694
|
+
);
|
|
1695
|
+
}
|
|
1696
|
+
processedFields.push(processedField);
|
|
1697
|
+
}
|
|
1698
|
+
return processedFields;
|
|
1699
|
+
}
|
|
1700
|
+
/**
|
|
1701
|
+
* Transform SDK TableQueryOptions to API request format
|
|
1702
|
+
*/
|
|
1703
|
+
transformTableQueryToApiRequest(options) {
|
|
1704
|
+
const apiRequest = {
|
|
1705
|
+
page: {
|
|
1706
|
+
page_no: 1,
|
|
1707
|
+
page_size: options.limit || 100
|
|
1708
|
+
},
|
|
1709
|
+
filters: [],
|
|
1710
|
+
sort: []
|
|
1711
|
+
};
|
|
1712
|
+
if (options.offset && options.limit) {
|
|
1713
|
+
const pageNo = Math.floor(options.offset / options.limit) + 1;
|
|
1714
|
+
apiRequest.page.page_no = pageNo;
|
|
1715
|
+
}
|
|
1716
|
+
if (options.where) {
|
|
1717
|
+
Object.entries(options.where).forEach(([field, value]) => {
|
|
1718
|
+
if (value !== void 0 && value !== null) {
|
|
1719
|
+
apiRequest.filters.push({
|
|
1720
|
+
field,
|
|
1721
|
+
operator: "=",
|
|
1722
|
+
values: [value]
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
if (options.sort) {
|
|
1728
|
+
apiRequest.sort = options.sort.map((s) => ({
|
|
1729
|
+
field: s.field,
|
|
1730
|
+
direction: s.order
|
|
1731
|
+
}));
|
|
1732
|
+
}
|
|
1733
|
+
return apiRequest;
|
|
1734
|
+
}
|
|
1735
|
+
/**
|
|
1736
|
+
* Find all tables with optional filtering
|
|
1737
|
+
*/
|
|
1738
|
+
async findAll(options = {}) {
|
|
1739
|
+
try {
|
|
1740
|
+
const apiRequest = this.transformTableQueryToApiRequest(options);
|
|
1741
|
+
const result = await this.tablesApiClient.listTables(
|
|
1742
|
+
apiRequest
|
|
1743
|
+
);
|
|
1744
|
+
if (isErrorResponse(result)) {
|
|
1745
|
+
throw new ApiError(
|
|
1746
|
+
result.error.message || "List tables failed",
|
|
1747
|
+
400,
|
|
1748
|
+
result.error
|
|
1749
|
+
);
|
|
1750
|
+
}
|
|
1751
|
+
return result;
|
|
1752
|
+
} catch (error) {
|
|
1753
|
+
throw error instanceof ApiError ? error : new ApiError(this.formatError(error), 500);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Find a single table by ID or name
|
|
1758
|
+
*/
|
|
1759
|
+
async findOne(options) {
|
|
1760
|
+
try {
|
|
1761
|
+
if (!options.where?.id && !options.where?.name) {
|
|
1762
|
+
throw new ValidationError(
|
|
1763
|
+
"Either id or name must be provided in where clause"
|
|
1764
|
+
);
|
|
1765
|
+
}
|
|
1766
|
+
if (options.where?.id) {
|
|
1767
|
+
const result = await this.tablesApiClient.getTable(
|
|
1768
|
+
options.where.id
|
|
1769
|
+
);
|
|
1770
|
+
if (isErrorResponse(result)) {
|
|
1771
|
+
if (result.error.code === "TABLE_NOT_FOUND") {
|
|
1772
|
+
return {
|
|
1773
|
+
data: null,
|
|
1774
|
+
message: "Table not found"
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1777
|
+
throw new ApiError(
|
|
1778
|
+
result.error.message || "Get table failed",
|
|
1779
|
+
400,
|
|
1780
|
+
result.error
|
|
1781
|
+
);
|
|
1782
|
+
}
|
|
1783
|
+
return result;
|
|
1784
|
+
} else {
|
|
1785
|
+
const apiRequest = {
|
|
1786
|
+
page: { page_no: 1, page_size: 1 },
|
|
1787
|
+
filters: [
|
|
1788
|
+
{
|
|
1789
|
+
field: "name",
|
|
1790
|
+
operator: "=",
|
|
1791
|
+
values: [options.where.name]
|
|
1792
|
+
}
|
|
1793
|
+
],
|
|
1794
|
+
sort: []
|
|
1795
|
+
};
|
|
1796
|
+
const listResult = await this.tablesApiClient.listTables(
|
|
1797
|
+
apiRequest
|
|
1798
|
+
);
|
|
1799
|
+
if (isErrorResponse(listResult)) {
|
|
1800
|
+
throw new ApiError(
|
|
1801
|
+
listResult.error.message || "Find table by name failed",
|
|
1802
|
+
400,
|
|
1803
|
+
listResult.error
|
|
1804
|
+
);
|
|
1805
|
+
}
|
|
1806
|
+
const table = isListResponse(listResult) ? listResult.data[0] : null;
|
|
1807
|
+
return {
|
|
1808
|
+
data: table || null,
|
|
1809
|
+
message: table ? "Table found" : "Table not found"
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
} catch (error) {
|
|
1813
|
+
throw error instanceof ApiError || error instanceof ValidationError ? error : new ApiError(this.formatError(error), 500);
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
/**
|
|
1817
|
+
* Find a single table by name
|
|
1818
|
+
*/
|
|
1819
|
+
async findByName(name) {
|
|
1820
|
+
return this.findOne({ where: { name } });
|
|
1821
|
+
}
|
|
1822
|
+
/**
|
|
1823
|
+
* Find a single table by ID
|
|
1824
|
+
*/
|
|
1825
|
+
async findById(id) {
|
|
1826
|
+
return this.findOne({ where: { id } });
|
|
1827
|
+
}
|
|
1828
|
+
/**
|
|
1829
|
+
* Update a table by name
|
|
1830
|
+
*/
|
|
1831
|
+
async update(name, data) {
|
|
1832
|
+
try {
|
|
1833
|
+
const tableResult = await this.findByName(name);
|
|
1834
|
+
if (!tableResult.data) {
|
|
1835
|
+
throw new ApiError(`Table '${name}' not found`, 404);
|
|
1836
|
+
}
|
|
1837
|
+
if (tableResult.data.snapshot_url) {
|
|
1838
|
+
throw new ApiError(
|
|
1839
|
+
`Cannot update snapshot table '${name}'. Snapshots are read-only and cannot be modified.`,
|
|
1840
|
+
400
|
|
1841
|
+
);
|
|
1842
|
+
}
|
|
1843
|
+
const result = await this.tablesApiClient.updateTable(
|
|
1844
|
+
tableResult.data.id,
|
|
1845
|
+
data
|
|
1846
|
+
);
|
|
1847
|
+
if (isErrorResponse(result)) {
|
|
1848
|
+
throw new ApiError(
|
|
1849
|
+
result.error.message || "Update table failed",
|
|
1850
|
+
400,
|
|
1851
|
+
result.error
|
|
1852
|
+
);
|
|
1853
|
+
}
|
|
1854
|
+
return result;
|
|
1855
|
+
} catch (error) {
|
|
1856
|
+
throw error instanceof ApiError ? error : new ApiError(this.formatError(error), 500);
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
/**
|
|
1860
|
+
* Delete a table by name
|
|
1861
|
+
*/
|
|
1862
|
+
async delete(name) {
|
|
1863
|
+
try {
|
|
1864
|
+
const tableResult = await this.findByName(name);
|
|
1865
|
+
if (!tableResult.data) {
|
|
1866
|
+
throw new ApiError(`Table '${name}' not found`, 404);
|
|
1867
|
+
}
|
|
1868
|
+
if (tableResult.data.snapshot_url) {
|
|
1869
|
+
throw new ApiError(
|
|
1870
|
+
`Cannot delete snapshot table '${name}'. Snapshots are read-only and cannot be deleted.`,
|
|
1871
|
+
400
|
|
1872
|
+
);
|
|
1873
|
+
}
|
|
1874
|
+
const result = await this.tablesApiClient.deleteTable(
|
|
1875
|
+
tableResult.data.id
|
|
1876
|
+
);
|
|
1877
|
+
if (isErrorResponse(result)) {
|
|
1878
|
+
throw new ApiError(
|
|
1879
|
+
result.error.message || "Delete table failed",
|
|
1880
|
+
400,
|
|
1881
|
+
result.error
|
|
1882
|
+
);
|
|
1883
|
+
}
|
|
1884
|
+
return result;
|
|
1885
|
+
} catch (error) {
|
|
1886
|
+
throw error instanceof ApiError ? error : new ApiError(this.formatError(error), 500);
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
/**
|
|
1890
|
+
* Rename a table
|
|
1891
|
+
*/
|
|
1892
|
+
async rename(oldName, newName) {
|
|
1893
|
+
try {
|
|
1894
|
+
const result = await this.update(oldName, {
|
|
1895
|
+
name: newName
|
|
1896
|
+
});
|
|
1897
|
+
return result;
|
|
1898
|
+
} catch (error) {
|
|
1899
|
+
throw error instanceof ApiError ? error : new ApiError(this.formatError(error), 500);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
/**
|
|
1903
|
+
* Set table access permissions
|
|
1904
|
+
*/
|
|
1905
|
+
async setAccess(request) {
|
|
1906
|
+
try {
|
|
1907
|
+
const result = await this.update(request.table_name, {
|
|
1908
|
+
is_shared: request.is_shared
|
|
1909
|
+
});
|
|
1910
|
+
return result;
|
|
1911
|
+
} catch (error) {
|
|
1912
|
+
throw error instanceof ApiError ? error : new ApiError(this.formatError(error), 500);
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
// Helper method to format generic errors
|
|
1916
|
+
formatError(error) {
|
|
1917
|
+
if (error instanceof Error) {
|
|
1918
|
+
return error.message;
|
|
1919
|
+
}
|
|
1920
|
+
if (typeof error === "string") {
|
|
1921
|
+
return error;
|
|
1922
|
+
}
|
|
1923
|
+
return "An unexpected error occurred";
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
class ColumnResource extends BaseResource {
|
|
1927
|
+
constructor(client) {
|
|
1928
|
+
super(client, "/v1/tables");
|
|
1929
|
+
const config = client.getConfig();
|
|
1930
|
+
this.columnsApiClient = new ColumnsApiClient(config.apiKey, {
|
|
1931
|
+
environment: config.environment,
|
|
1932
|
+
timeout: config.timeout,
|
|
1933
|
+
debug: config.debug,
|
|
1934
|
+
retryAttempts: config.retryAttempts,
|
|
1935
|
+
retryDelay: config.retryDelay,
|
|
1936
|
+
headers: config.headers
|
|
1937
|
+
});
|
|
1938
|
+
this.tablesApiClient = new TablesApiClient(config.apiKey, {
|
|
1939
|
+
environment: config.environment,
|
|
1940
|
+
timeout: config.timeout,
|
|
1941
|
+
debug: config.debug,
|
|
1942
|
+
retryAttempts: config.retryAttempts,
|
|
1943
|
+
retryDelay: config.retryDelay,
|
|
1944
|
+
headers: config.headers
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1947
|
+
/**
|
|
1948
|
+
* Create a single column in a table
|
|
1949
|
+
*/
|
|
1950
|
+
async create(tableName, column) {
|
|
1951
|
+
try {
|
|
1952
|
+
const tableInfo = await this.getTableInfo(tableName);
|
|
1953
|
+
if (!tableInfo) {
|
|
1954
|
+
return {
|
|
1955
|
+
error: {
|
|
1956
|
+
code: "TABLE_NOT_FOUND",
|
|
1957
|
+
message: `Table '${tableName}' not found`
|
|
1958
|
+
}
|
|
1959
|
+
};
|
|
1960
|
+
}
|
|
1961
|
+
const processedColumn = await this.processColumnDefaults(
|
|
1962
|
+
tableInfo.id,
|
|
1963
|
+
column
|
|
1964
|
+
);
|
|
1965
|
+
const result = await this.columnsApiClient.createColumn(
|
|
1966
|
+
tableInfo.id,
|
|
1967
|
+
processedColumn
|
|
1968
|
+
);
|
|
1969
|
+
if (isErrorResponse(result)) {
|
|
1970
|
+
return result;
|
|
1971
|
+
}
|
|
1972
|
+
return result;
|
|
1973
|
+
} catch (error) {
|
|
1974
|
+
return {
|
|
1975
|
+
error: {
|
|
1976
|
+
code: "CREATE_COLUMN_ERROR",
|
|
1977
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
1978
|
+
}
|
|
1979
|
+
};
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
/**
|
|
1983
|
+
* Process column defaults and auto-generate field_order
|
|
1984
|
+
*/
|
|
1985
|
+
async processColumnDefaults(tableId, column) {
|
|
1986
|
+
const processedColumn = { ...column };
|
|
1987
|
+
if (processedColumn.is_primary_key === void 0) {
|
|
1988
|
+
processedColumn.is_primary_key = false;
|
|
1989
|
+
}
|
|
1990
|
+
if (processedColumn.is_unique === void 0) {
|
|
1991
|
+
processedColumn.is_unique = false;
|
|
1992
|
+
}
|
|
1993
|
+
if (processedColumn.is_nullable === void 0) {
|
|
1994
|
+
processedColumn.is_nullable = true;
|
|
1995
|
+
}
|
|
1996
|
+
if (processedColumn.is_indexed === void 0) {
|
|
1997
|
+
processedColumn.is_indexed = false;
|
|
1998
|
+
}
|
|
1999
|
+
if (processedColumn.field_order === void 0) {
|
|
2000
|
+
processedColumn.field_order = await this.generateFieldOrder(tableId);
|
|
2001
|
+
}
|
|
2002
|
+
if (processedColumn.field_order <= 0 || processedColumn.field_order >= 2147483647) {
|
|
2003
|
+
throw new Error(
|
|
2004
|
+
"Field order must be a number greater than 0 and less than 2147483647"
|
|
2005
|
+
);
|
|
2006
|
+
}
|
|
2007
|
+
return processedColumn;
|
|
2008
|
+
}
|
|
2009
|
+
/**
|
|
2010
|
+
* Generate the next available field_order for a table
|
|
2011
|
+
*/
|
|
2012
|
+
async generateFieldOrder(tableId) {
|
|
2013
|
+
try {
|
|
2014
|
+
const existingColumns = await this.columnsApiClient.listColumns(tableId);
|
|
2015
|
+
let maxOrder = 0;
|
|
2016
|
+
if (!isErrorResponse(existingColumns) && existingColumns.data && Array.isArray(existingColumns.data)) {
|
|
2017
|
+
for (const col of existingColumns.data) {
|
|
2018
|
+
if (col.field_order && col.field_order > maxOrder) {
|
|
2019
|
+
maxOrder = col.field_order;
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
return maxOrder + 1;
|
|
2024
|
+
} catch (error) {
|
|
2025
|
+
return Math.floor(Date.now() / 1e3) % 2147483647;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
/**
|
|
2029
|
+
* Transform SDK ColumnQueryOptions to API request format
|
|
2030
|
+
*/
|
|
2031
|
+
transformColumnQueryToApiRequest(options) {
|
|
2032
|
+
const apiRequest = {
|
|
2033
|
+
page: {
|
|
2034
|
+
page_no: 1,
|
|
2035
|
+
page_size: options.limit || 100
|
|
2036
|
+
},
|
|
2037
|
+
filters: [],
|
|
2038
|
+
sort: []
|
|
2039
|
+
};
|
|
2040
|
+
if (options.offset && options.limit) {
|
|
2041
|
+
const pageNo = Math.floor(options.offset / options.limit) + 1;
|
|
2042
|
+
apiRequest.page.page_no = pageNo;
|
|
2043
|
+
}
|
|
2044
|
+
if (options.where) {
|
|
2045
|
+
Object.entries(options.where).forEach(([field, value]) => {
|
|
2046
|
+
if (value !== void 0 && value !== null) {
|
|
2047
|
+
apiRequest.filters.push({
|
|
2048
|
+
field,
|
|
2049
|
+
operator: "=",
|
|
2050
|
+
values: [value]
|
|
2051
|
+
});
|
|
2052
|
+
}
|
|
2053
|
+
});
|
|
2054
|
+
}
|
|
2055
|
+
if (options.sort) {
|
|
2056
|
+
apiRequest.sort = options.sort.map((s) => ({
|
|
2057
|
+
field: s.field,
|
|
2058
|
+
direction: s.order
|
|
2059
|
+
}));
|
|
2060
|
+
}
|
|
2061
|
+
return apiRequest;
|
|
2062
|
+
}
|
|
2063
|
+
/**
|
|
2064
|
+
* Add multiple columns to existing table
|
|
2065
|
+
*/
|
|
2066
|
+
async createMany(tableName, columns) {
|
|
2067
|
+
try {
|
|
2068
|
+
const tableInfo = await this.getTableInfo(tableName);
|
|
2069
|
+
if (!tableInfo) {
|
|
2070
|
+
return {
|
|
2071
|
+
error: {
|
|
2072
|
+
code: "TABLE_NOT_FOUND",
|
|
2073
|
+
message: `Table '${tableName}' not found`
|
|
2074
|
+
}
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
const processedColumns = [];
|
|
2078
|
+
for (const column of columns) {
|
|
2079
|
+
const processedColumn = await this.processColumnDefaults(
|
|
2080
|
+
tableInfo.id,
|
|
2081
|
+
column
|
|
2082
|
+
);
|
|
2083
|
+
processedColumns.push(processedColumn);
|
|
2084
|
+
}
|
|
2085
|
+
const result = await this.columnsApiClient.createColumns(tableInfo.id, {
|
|
2086
|
+
columns: processedColumns
|
|
2087
|
+
});
|
|
2088
|
+
if (isErrorResponse(result)) {
|
|
2089
|
+
return result;
|
|
2090
|
+
}
|
|
2091
|
+
return result;
|
|
2092
|
+
} catch (error) {
|
|
2093
|
+
return {
|
|
2094
|
+
error: {
|
|
2095
|
+
code: "CREATE_COLUMNS_ERROR",
|
|
2096
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2097
|
+
}
|
|
2098
|
+
};
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
/**
|
|
2102
|
+
* Find all columns in a table (replaces list functionality)
|
|
2103
|
+
*/
|
|
2104
|
+
async findAll(tableName, options = {}) {
|
|
2105
|
+
try {
|
|
2106
|
+
const tableInfo = await this.getTableInfo(tableName);
|
|
2107
|
+
if (!tableInfo) {
|
|
2108
|
+
return {
|
|
2109
|
+
error: {
|
|
2110
|
+
code: "TABLE_NOT_FOUND",
|
|
2111
|
+
message: `Table '${tableName}' not found`
|
|
2112
|
+
}
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
const apiRequest = this.transformColumnQueryToApiRequest(options);
|
|
2116
|
+
const result = await this.columnsApiClient.listColumns(
|
|
2117
|
+
tableInfo.id,
|
|
2118
|
+
apiRequest
|
|
2119
|
+
);
|
|
2120
|
+
if (isErrorResponse(result)) {
|
|
2121
|
+
return result;
|
|
2122
|
+
}
|
|
2123
|
+
return result;
|
|
2124
|
+
} catch (error) {
|
|
2125
|
+
return {
|
|
2126
|
+
error: {
|
|
2127
|
+
code: "LIST_COLUMNS_ERROR",
|
|
2128
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2129
|
+
}
|
|
2130
|
+
};
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
/**
|
|
2134
|
+
* Get a single column by name
|
|
2135
|
+
*/
|
|
2136
|
+
async get(tableName, columnName) {
|
|
2137
|
+
try {
|
|
2138
|
+
const tableInfo = await this.getTableInfo(tableName);
|
|
2139
|
+
if (!tableInfo) {
|
|
2140
|
+
return {
|
|
2141
|
+
error: {
|
|
2142
|
+
code: "TABLE_NOT_FOUND",
|
|
2143
|
+
message: `Table '${tableName}' not found`
|
|
2144
|
+
}
|
|
2145
|
+
};
|
|
2146
|
+
}
|
|
2147
|
+
const result = await this.columnsApiClient.findColumnByName(
|
|
2148
|
+
tableInfo.id,
|
|
2149
|
+
columnName
|
|
2150
|
+
);
|
|
2151
|
+
if (isErrorResponse(result)) {
|
|
2152
|
+
return result;
|
|
2153
|
+
}
|
|
2154
|
+
if (!result.data) {
|
|
2155
|
+
return {
|
|
2156
|
+
error: {
|
|
2157
|
+
code: "COLUMN_NOT_FOUND",
|
|
2158
|
+
message: `Column '${columnName}' not found in table '${tableName}'`
|
|
2159
|
+
}
|
|
2160
|
+
};
|
|
2161
|
+
}
|
|
2162
|
+
return {
|
|
2163
|
+
data: result.data,
|
|
2164
|
+
message: "Column found successfully"
|
|
2165
|
+
};
|
|
2166
|
+
} catch (error) {
|
|
2167
|
+
return {
|
|
2168
|
+
error: {
|
|
2169
|
+
code: "GET_COLUMN_ERROR",
|
|
2170
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2171
|
+
}
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
async findById(tableName, columnId) {
|
|
2176
|
+
try {
|
|
2177
|
+
const tableInfo = await this.getTableInfo(tableName);
|
|
2178
|
+
if (!tableInfo) {
|
|
2179
|
+
return {
|
|
2180
|
+
error: {
|
|
2181
|
+
code: "TABLE_NOT_FOUND",
|
|
2182
|
+
message: `Table '${tableName}' not found`
|
|
2183
|
+
}
|
|
2184
|
+
};
|
|
2185
|
+
}
|
|
2186
|
+
const result = await this.columnsApiClient.getColumn(
|
|
2187
|
+
tableInfo.id,
|
|
2188
|
+
columnId
|
|
2189
|
+
);
|
|
2190
|
+
if (isErrorResponse(result)) {
|
|
2191
|
+
return result;
|
|
2192
|
+
}
|
|
2193
|
+
return result;
|
|
2194
|
+
} catch (error) {
|
|
2195
|
+
return {
|
|
2196
|
+
error: {
|
|
2197
|
+
code: "FIND_COLUMN_BY_ID_ERROR",
|
|
2198
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2199
|
+
}
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
/**
|
|
2204
|
+
* Update a column by name
|
|
2205
|
+
*/
|
|
2206
|
+
async update(tableName, columnName, updates) {
|
|
2207
|
+
try {
|
|
2208
|
+
const tableInfo = await this.getTableInfo(tableName);
|
|
2209
|
+
if (!tableInfo) {
|
|
2210
|
+
return {
|
|
2211
|
+
error: {
|
|
2212
|
+
code: "TABLE_NOT_FOUND",
|
|
2213
|
+
message: `Table '${tableName}' not found`
|
|
2214
|
+
}
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
const result = await this.columnsApiClient.updateColumnByName(
|
|
2218
|
+
tableInfo.id,
|
|
2219
|
+
columnName,
|
|
2220
|
+
updates
|
|
2221
|
+
);
|
|
2222
|
+
if (isErrorResponse(result)) {
|
|
2223
|
+
return result;
|
|
2224
|
+
}
|
|
2225
|
+
return result;
|
|
2226
|
+
} catch (error) {
|
|
2227
|
+
return {
|
|
2228
|
+
error: {
|
|
2229
|
+
code: "UPDATE_COLUMN_ERROR",
|
|
2230
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2231
|
+
}
|
|
2232
|
+
};
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
/**
|
|
2236
|
+
* Delete a column by name
|
|
2237
|
+
*/
|
|
2238
|
+
async delete(tableName, columnName) {
|
|
2239
|
+
try {
|
|
2240
|
+
const tableInfo = await this.getTableInfo(tableName);
|
|
2241
|
+
if (!tableInfo) {
|
|
2242
|
+
return {
|
|
2243
|
+
error: {
|
|
2244
|
+
code: "TABLE_NOT_FOUND",
|
|
2245
|
+
message: `Table '${tableName}' not found`
|
|
2246
|
+
}
|
|
2247
|
+
};
|
|
2248
|
+
}
|
|
2249
|
+
const result = await this.columnsApiClient.deleteColumnByName(
|
|
2250
|
+
tableInfo.id,
|
|
2251
|
+
columnName
|
|
2252
|
+
);
|
|
2253
|
+
if (isErrorResponse(result)) {
|
|
2254
|
+
return result;
|
|
2255
|
+
}
|
|
2256
|
+
return {
|
|
2257
|
+
data: {
|
|
2258
|
+
success: true,
|
|
2259
|
+
message: "Column deleted successfully"
|
|
2260
|
+
}
|
|
2261
|
+
};
|
|
2262
|
+
} catch (error) {
|
|
2263
|
+
return {
|
|
2264
|
+
error: {
|
|
2265
|
+
code: "DELETE_COLUMN_ERROR",
|
|
2266
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2267
|
+
}
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
/**
|
|
2272
|
+
* Helper method to get table information by name
|
|
2273
|
+
*/
|
|
2274
|
+
async getTableInfo(tableName) {
|
|
2275
|
+
try {
|
|
2276
|
+
const tableResource = new TableResource(this.client);
|
|
2277
|
+
const tableResult = await tableResource.findByName(tableName);
|
|
2278
|
+
if (tableResult.data) {
|
|
2279
|
+
return {
|
|
2280
|
+
id: tableResult.data.id,
|
|
2281
|
+
snapshot_url: tableResult.data.snapshot_url
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
return null;
|
|
2285
|
+
} catch (error) {
|
|
2286
|
+
console.error("Error getting table info:", error);
|
|
2287
|
+
return null;
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
/**
|
|
2291
|
+
* Helper method to get table ID by name (deprecated, use getTableInfo instead)
|
|
2292
|
+
*/
|
|
2293
|
+
async getTableId(tableName) {
|
|
2294
|
+
const tableInfo = await this.getTableInfo(tableName);
|
|
2295
|
+
return tableInfo?.id || null;
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
const RECORD_ENDPOINTS = {
|
|
2299
|
+
insert: {
|
|
2300
|
+
path: "/tables/{table_id}/records",
|
|
2301
|
+
method: "POST",
|
|
2302
|
+
authenticated: true
|
|
2303
|
+
},
|
|
2304
|
+
insertMany: {
|
|
2305
|
+
path: "/tables/{table_id}/records/bulk-insert",
|
|
2306
|
+
method: "POST",
|
|
2307
|
+
authenticated: true
|
|
2308
|
+
},
|
|
2309
|
+
list: {
|
|
2310
|
+
path: "/tables/{table_id}/records/list",
|
|
2311
|
+
method: "POST",
|
|
2312
|
+
authenticated: true,
|
|
2313
|
+
rateLimit: { requests: 200, window: 6e4 }
|
|
2314
|
+
},
|
|
2315
|
+
get: {
|
|
2316
|
+
path: "/tables/{table_id}/records/{record_id}",
|
|
2317
|
+
method: "GET",
|
|
2318
|
+
authenticated: true,
|
|
2319
|
+
rateLimit: { requests: 200, window: 6e4 }
|
|
2320
|
+
},
|
|
2321
|
+
update: {
|
|
2322
|
+
path: "/tables/{table_id}/records",
|
|
2323
|
+
method: "PATCH",
|
|
2324
|
+
authenticated: true
|
|
2325
|
+
},
|
|
2326
|
+
updateById: {
|
|
2327
|
+
path: "/tables/{table_id}/records/{record_id}",
|
|
2328
|
+
method: "PATCH",
|
|
2329
|
+
authenticated: true
|
|
2330
|
+
},
|
|
2331
|
+
delete: {
|
|
2332
|
+
path: "/tables/{table_id}/records/list",
|
|
2333
|
+
method: "DELETE",
|
|
2334
|
+
authenticated: true
|
|
2335
|
+
}
|
|
2336
|
+
};
|
|
2337
|
+
const buildRecordEndpointPath = (endpoint, params = {}) => {
|
|
2338
|
+
let path = endpoint.path;
|
|
2339
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
2340
|
+
path = path.replace(`{${key}}`, encodeURIComponent(value));
|
|
2341
|
+
});
|
|
2342
|
+
const unreplacedParams = path.match(/\{([^}]+)\}/g);
|
|
2343
|
+
if (unreplacedParams) {
|
|
2344
|
+
throw new Error(`Missing path parameters: ${unreplacedParams.join(", ")}`);
|
|
2345
|
+
}
|
|
2346
|
+
return path;
|
|
2347
|
+
};
|
|
2348
|
+
function transformDeleteRequest(sdkRequest) {
|
|
2349
|
+
const result = {};
|
|
2350
|
+
if (sdkRequest.record_ids && sdkRequest.record_ids.length > 0) {
|
|
2351
|
+
result.record_ids = sdkRequest.record_ids;
|
|
2352
|
+
}
|
|
2353
|
+
if (sdkRequest.filters) {
|
|
2354
|
+
if (Array.isArray(sdkRequest.filters)) {
|
|
2355
|
+
if (sdkRequest.filters.length > 0 && typeof sdkRequest.filters[0] === "object" && "field" in sdkRequest.filters[0] && "operator" in sdkRequest.filters[0] && "values" in sdkRequest.filters[0]) {
|
|
2356
|
+
result.filters = sdkRequest.filters;
|
|
2357
|
+
} else {
|
|
2358
|
+
console.warn(
|
|
2359
|
+
"Legacy Record<string, unknown>[] filter format detected. Please migrate to the new filter format."
|
|
2360
|
+
);
|
|
2361
|
+
result.filters = sdkRequest.filters;
|
|
2362
|
+
}
|
|
2363
|
+
} else {
|
|
2364
|
+
result.filters = mapWhereToFilters(sdkRequest.filters);
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
return result;
|
|
2368
|
+
}
|
|
2369
|
+
class RecordsApiClient {
|
|
2370
|
+
constructor(apiKey, config = {}) {
|
|
2371
|
+
this.config = { apiKey, ...config };
|
|
2372
|
+
this.httpAdapter = createHttpAdapter();
|
|
2373
|
+
const environment = config.environment || "prod";
|
|
2374
|
+
const region = config.region || "asia-south1";
|
|
2375
|
+
this.baseURL = this.getBaseURL(environment, region);
|
|
2376
|
+
}
|
|
2377
|
+
getBaseURL(environment, region) {
|
|
2378
|
+
const regionConfig = REGION_CONFIGS[region];
|
|
2379
|
+
if (!regionConfig) {
|
|
2380
|
+
throw new Error(`Unsupported region: ${region}`);
|
|
2381
|
+
}
|
|
2382
|
+
const envConfig = regionConfig[environment];
|
|
2383
|
+
if (!envConfig) {
|
|
2384
|
+
throw new Error(
|
|
2385
|
+
`Unsupported environment: ${environment} for region: ${region}`
|
|
2386
|
+
);
|
|
2387
|
+
}
|
|
2388
|
+
return `${envConfig.baseURL}/v1`;
|
|
2389
|
+
}
|
|
2390
|
+
/**
|
|
2391
|
+
* Insert a single record
|
|
2392
|
+
*/
|
|
2393
|
+
async insertRecord(request) {
|
|
2394
|
+
try {
|
|
2395
|
+
const { table_id, fields, ...recordData } = request;
|
|
2396
|
+
if (!table_id) {
|
|
2397
|
+
return this.formatErrorResponse(
|
|
2398
|
+
new Error("table_id is required for insert operation")
|
|
2399
|
+
);
|
|
2400
|
+
}
|
|
2401
|
+
const endpoint = RECORD_ENDPOINTS.insert;
|
|
2402
|
+
const url = `${this.baseURL}${buildRecordEndpointPath(endpoint, { table_id })}`;
|
|
2403
|
+
const response = await this.httpAdapter.request({
|
|
2404
|
+
url,
|
|
2405
|
+
method: endpoint.method,
|
|
2406
|
+
headers: this.buildHeaders(),
|
|
2407
|
+
data: recordData,
|
|
2408
|
+
timeout: this.config.timeout
|
|
2409
|
+
});
|
|
2410
|
+
const responseData = response.data;
|
|
2411
|
+
if (fields && responseData.data) {
|
|
2412
|
+
responseData.data = filterObjectFields(
|
|
2413
|
+
responseData.data,
|
|
2414
|
+
fields
|
|
2415
|
+
);
|
|
2416
|
+
}
|
|
2417
|
+
return responseData;
|
|
2418
|
+
} catch (error) {
|
|
2419
|
+
return this.formatErrorResponse(error);
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
/**
|
|
2423
|
+
* Insert multiple records in bulk
|
|
2424
|
+
*/
|
|
2425
|
+
async insertManyRecords(records, tableId, options = { validation: true }) {
|
|
2426
|
+
try {
|
|
2427
|
+
if (!tableId) {
|
|
2428
|
+
return this.formatErrorResponse(
|
|
2429
|
+
new Error("table_id is required for bulk insert operation")
|
|
2430
|
+
);
|
|
2431
|
+
}
|
|
2432
|
+
if (!records || !Array.isArray(records) || records.length === 0) {
|
|
2433
|
+
return this.formatErrorResponse(
|
|
2434
|
+
new Error("records array is required and cannot be empty")
|
|
2435
|
+
);
|
|
2436
|
+
}
|
|
2437
|
+
const endpoint = RECORD_ENDPOINTS.insertMany;
|
|
2438
|
+
let url = `${this.baseURL}${buildRecordEndpointPath(endpoint, { table_id: tableId })}`;
|
|
2439
|
+
const queryParams = new URLSearchParams();
|
|
2440
|
+
if (options.validation !== void 0) {
|
|
2441
|
+
queryParams.append("validation", options.validation.toString());
|
|
2442
|
+
}
|
|
2443
|
+
if (queryParams.toString()) {
|
|
2444
|
+
url += `?${queryParams.toString()}`;
|
|
2445
|
+
}
|
|
2446
|
+
const response = await this.httpAdapter.request({
|
|
2447
|
+
url,
|
|
2448
|
+
method: endpoint.method,
|
|
2449
|
+
headers: this.buildHeaders(),
|
|
2450
|
+
data: records,
|
|
2451
|
+
timeout: this.config.timeout
|
|
2452
|
+
});
|
|
2453
|
+
return response.data;
|
|
2454
|
+
} catch (error) {
|
|
2455
|
+
return this.formatErrorResponse(error);
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
/**
|
|
2459
|
+
* Get a single record by ID
|
|
2460
|
+
*/
|
|
2461
|
+
async getRecord(recordId, tableId, options = {}) {
|
|
2462
|
+
try {
|
|
2463
|
+
if (!tableId) {
|
|
2464
|
+
return this.formatErrorResponse(
|
|
2465
|
+
new Error("table_id is required for get operation")
|
|
2466
|
+
);
|
|
2467
|
+
}
|
|
2468
|
+
const endpoint = RECORD_ENDPOINTS.get;
|
|
2469
|
+
const url = `${this.baseURL}${buildRecordEndpointPath(endpoint, {
|
|
2470
|
+
table_id: tableId,
|
|
2471
|
+
record_id: recordId
|
|
2472
|
+
})}`;
|
|
2473
|
+
const response = await this.httpAdapter.request({
|
|
2474
|
+
url,
|
|
2475
|
+
method: endpoint.method,
|
|
2476
|
+
headers: this.buildHeaders(),
|
|
2477
|
+
timeout: this.config.timeout
|
|
2478
|
+
});
|
|
2479
|
+
const responseData = response.data;
|
|
2480
|
+
if (options.fields && responseData.data) {
|
|
2481
|
+
responseData.data = filterObjectFields(
|
|
2482
|
+
responseData.data,
|
|
2483
|
+
options.fields
|
|
2484
|
+
);
|
|
2485
|
+
}
|
|
2486
|
+
return responseData;
|
|
2487
|
+
} catch (error) {
|
|
2488
|
+
return this.formatErrorResponse(error);
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
/**
|
|
2492
|
+
* List records with filtering and pagination
|
|
2493
|
+
*/
|
|
2494
|
+
async listRecords(options = {}) {
|
|
2495
|
+
try {
|
|
2496
|
+
const { table_id, ...queryOptions } = options;
|
|
2497
|
+
if (!table_id) {
|
|
2498
|
+
return this.formatErrorResponse(
|
|
2499
|
+
new Error("table_id is required for list operation")
|
|
2500
|
+
);
|
|
2501
|
+
}
|
|
2502
|
+
const endpoint = RECORD_ENDPOINTS.list;
|
|
2503
|
+
const url = `${this.baseURL}${buildRecordEndpointPath(endpoint, { table_id })}`;
|
|
2504
|
+
const response = await this.httpAdapter.request({
|
|
2505
|
+
url,
|
|
2506
|
+
method: endpoint.method,
|
|
2507
|
+
headers: this.buildHeaders(),
|
|
2508
|
+
data: queryOptions,
|
|
2509
|
+
timeout: this.config.timeout
|
|
2510
|
+
});
|
|
2511
|
+
const responseData = response.data;
|
|
2512
|
+
if (queryOptions.fields && responseData.data) {
|
|
2513
|
+
responseData.data = filterArrayFields(
|
|
2514
|
+
responseData.data,
|
|
2515
|
+
queryOptions.fields
|
|
2516
|
+
);
|
|
2517
|
+
}
|
|
2518
|
+
return responseData;
|
|
2519
|
+
} catch (error) {
|
|
2520
|
+
return this.formatErrorResponse(error);
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
/**
|
|
2524
|
+
* Update records by filters
|
|
2525
|
+
*/
|
|
2526
|
+
async updateRecords(request) {
|
|
2527
|
+
try {
|
|
2528
|
+
const { table_id, ...updateOptions } = request;
|
|
2529
|
+
if (!table_id) {
|
|
2530
|
+
return this.formatErrorResponse(
|
|
2531
|
+
new Error("table_id is required for update operation")
|
|
2532
|
+
);
|
|
2533
|
+
}
|
|
2534
|
+
const endpoint = RECORD_ENDPOINTS.update;
|
|
2535
|
+
const url = `${this.baseURL}${buildRecordEndpointPath(endpoint, { table_id })}`;
|
|
2536
|
+
const response = await this.httpAdapter.request({
|
|
2537
|
+
url,
|
|
2538
|
+
method: endpoint.method,
|
|
2539
|
+
headers: this.buildHeaders(),
|
|
2540
|
+
data: updateOptions,
|
|
2541
|
+
timeout: this.config.timeout
|
|
2542
|
+
});
|
|
2543
|
+
const responseData = response.data;
|
|
2544
|
+
if (updateOptions.fields && responseData.data) {
|
|
2545
|
+
responseData.data = filterArrayFields(
|
|
2546
|
+
responseData.data,
|
|
2547
|
+
updateOptions.fields
|
|
2548
|
+
);
|
|
2549
|
+
}
|
|
2550
|
+
return responseData;
|
|
2551
|
+
} catch (error) {
|
|
2552
|
+
return this.formatErrorResponse(error);
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
/**
|
|
2556
|
+
* Update a single record by ID
|
|
2557
|
+
*/
|
|
2558
|
+
async updateRecordById(recordId, request) {
|
|
2559
|
+
try {
|
|
2560
|
+
const { table_id, ...updateOptions } = request;
|
|
2561
|
+
if (!table_id) {
|
|
2562
|
+
return this.formatErrorResponse(
|
|
2563
|
+
new Error("table_id is required for updateById operation")
|
|
2564
|
+
);
|
|
2565
|
+
}
|
|
2566
|
+
const endpoint = RECORD_ENDPOINTS.updateById;
|
|
2567
|
+
const url = `${this.baseURL}${buildRecordEndpointPath(endpoint, {
|
|
2568
|
+
record_id: recordId,
|
|
2569
|
+
table_id
|
|
2570
|
+
})}`;
|
|
2571
|
+
const response = await this.httpAdapter.request({
|
|
2572
|
+
url,
|
|
2573
|
+
method: endpoint.method,
|
|
2574
|
+
headers: this.buildHeaders(),
|
|
2575
|
+
data: updateOptions.set,
|
|
2576
|
+
timeout: this.config.timeout
|
|
2577
|
+
});
|
|
2578
|
+
const responseData = response.data;
|
|
2579
|
+
if (updateOptions.fields && responseData.data) {
|
|
2580
|
+
responseData.data = filterObjectFields(
|
|
2581
|
+
responseData.data,
|
|
2582
|
+
updateOptions.fields
|
|
2583
|
+
);
|
|
2584
|
+
}
|
|
2585
|
+
return responseData;
|
|
2586
|
+
} catch (error) {
|
|
2587
|
+
return this.formatErrorResponse(error);
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
/**
|
|
2591
|
+
* Unified delete records method that supports both record_ids and filters
|
|
2592
|
+
*/
|
|
2593
|
+
async deleteRecords(request) {
|
|
2594
|
+
try {
|
|
2595
|
+
const { table_id } = request;
|
|
2596
|
+
if (!table_id) {
|
|
2597
|
+
return this.formatErrorResponse(
|
|
2598
|
+
new Error("table_id is required for delete operation")
|
|
2599
|
+
);
|
|
2600
|
+
}
|
|
2601
|
+
const transformedRequest = transformDeleteRequest(request);
|
|
2602
|
+
const endpoint = RECORD_ENDPOINTS.delete;
|
|
2603
|
+
const url = `${this.baseURL}${buildRecordEndpointPath(endpoint, { table_id })}`;
|
|
2604
|
+
const response = await this.httpAdapter.request({
|
|
2605
|
+
url,
|
|
2606
|
+
method: endpoint.method,
|
|
2607
|
+
headers: this.buildHeaders(),
|
|
2608
|
+
data: transformedRequest,
|
|
2609
|
+
timeout: this.config.timeout
|
|
2610
|
+
});
|
|
2611
|
+
return response.data;
|
|
2612
|
+
} catch (error) {
|
|
2613
|
+
return this.formatErrorResponse(error);
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
/**
|
|
2617
|
+
* Delete a single record by ID
|
|
2618
|
+
*/
|
|
2619
|
+
async deleteRecordById(recordId, request) {
|
|
2620
|
+
return this.deleteRecords({
|
|
2621
|
+
record_ids: [recordId],
|
|
2622
|
+
table_id: request.table_id
|
|
2623
|
+
});
|
|
2624
|
+
}
|
|
2625
|
+
buildHeaders() {
|
|
2626
|
+
return {
|
|
2627
|
+
"Content-Type": "application/json",
|
|
2628
|
+
Accept: "application/json",
|
|
2629
|
+
"x-boltic-token": this.config.apiKey
|
|
2630
|
+
};
|
|
2631
|
+
}
|
|
2632
|
+
formatErrorResponse(error) {
|
|
2633
|
+
if (this.config.debug) {
|
|
2634
|
+
console.error("Records API Error:", error);
|
|
2635
|
+
}
|
|
2636
|
+
if (error && typeof error === "object" && "response" in error) {
|
|
2637
|
+
const apiError = error;
|
|
2638
|
+
if (apiError.response?.data?.error) {
|
|
2639
|
+
return apiError.response.data;
|
|
2640
|
+
}
|
|
2641
|
+
return {
|
|
2642
|
+
error: {
|
|
2643
|
+
code: "API_ERROR",
|
|
2644
|
+
message: error.message || "Unknown API error",
|
|
2645
|
+
meta: [`Status: ${apiError.response?.status || "unknown"}`]
|
|
2646
|
+
}
|
|
2647
|
+
};
|
|
2648
|
+
}
|
|
2649
|
+
if (error && typeof error === "object" && "message" in error) {
|
|
2650
|
+
return {
|
|
2651
|
+
error: {
|
|
2652
|
+
code: "CLIENT_ERROR",
|
|
2653
|
+
message: error.message,
|
|
2654
|
+
meta: ["Client-side error occurred"]
|
|
2655
|
+
}
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2658
|
+
return {
|
|
2659
|
+
error: {
|
|
2660
|
+
code: "UNKNOWN_ERROR",
|
|
2661
|
+
message: "An unexpected error occurred",
|
|
2662
|
+
meta: ["Unknown error type"]
|
|
2663
|
+
}
|
|
2664
|
+
};
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
class RecordResource {
|
|
2668
|
+
constructor(client) {
|
|
2669
|
+
this.client = client;
|
|
2670
|
+
this.apiClient = new RecordsApiClient(client.getConfig().apiKey, {
|
|
2671
|
+
environment: client.getConfig().environment,
|
|
2672
|
+
timeout: client.getConfig().timeout,
|
|
2673
|
+
debug: client.getConfig().debug
|
|
2674
|
+
});
|
|
2675
|
+
this.tablesApiClient = new TablesApiClient(client.getConfig().apiKey, {
|
|
2676
|
+
environment: client.getConfig().environment,
|
|
2677
|
+
timeout: client.getConfig().timeout,
|
|
2678
|
+
debug: client.getConfig().debug
|
|
2679
|
+
});
|
|
2680
|
+
}
|
|
2681
|
+
/**
|
|
2682
|
+
* Insert a single record
|
|
2683
|
+
*/
|
|
2684
|
+
async insert(tableName, data) {
|
|
2685
|
+
try {
|
|
2686
|
+
const tableInfo = await this.getTableInfo(tableName);
|
|
2687
|
+
if (!tableInfo) {
|
|
2688
|
+
return {
|
|
2689
|
+
error: {
|
|
2690
|
+
code: "TABLE_NOT_FOUND",
|
|
2691
|
+
message: `Table '${tableName}' not found`
|
|
2692
|
+
}
|
|
2693
|
+
};
|
|
2694
|
+
}
|
|
2695
|
+
const completeDataResult = await this.ensureCompleteRecordData(
|
|
2696
|
+
tableName,
|
|
2697
|
+
data
|
|
2698
|
+
);
|
|
2699
|
+
if ("error" in completeDataResult && completeDataResult.error) {
|
|
2700
|
+
return completeDataResult;
|
|
2701
|
+
}
|
|
2702
|
+
const requestData = {
|
|
2703
|
+
...completeDataResult,
|
|
2704
|
+
table_id: tableInfo.id
|
|
2705
|
+
};
|
|
2706
|
+
const result = await this.apiClient.insertRecord(requestData);
|
|
2707
|
+
if (isErrorResponse(result)) {
|
|
2708
|
+
return result;
|
|
2709
|
+
}
|
|
2710
|
+
return result;
|
|
2711
|
+
} catch (error) {
|
|
2712
|
+
return {
|
|
2713
|
+
error: {
|
|
2714
|
+
code: "INSERT_ERROR",
|
|
2715
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2716
|
+
}
|
|
2717
|
+
};
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
/**
|
|
2721
|
+
* Insert multiple records in bulk
|
|
2722
|
+
*/
|
|
2723
|
+
async insertMany(tableName, records, options = { validation: true }) {
|
|
2724
|
+
try {
|
|
2725
|
+
if (!records || !Array.isArray(records) || records.length === 0) {
|
|
2726
|
+
return {
|
|
2727
|
+
error: {
|
|
2728
|
+
code: "INVALID_INPUT",
|
|
2729
|
+
message: "Records array is required and cannot be empty"
|
|
2730
|
+
}
|
|
2731
|
+
};
|
|
2732
|
+
}
|
|
2733
|
+
const tableInfo = await this.getTableInfo(tableName);
|
|
2734
|
+
if (!tableInfo) {
|
|
2735
|
+
return {
|
|
2736
|
+
error: {
|
|
2737
|
+
code: "TABLE_NOT_FOUND",
|
|
2738
|
+
message: `Table '${tableName}' not found`
|
|
2739
|
+
}
|
|
2740
|
+
};
|
|
2741
|
+
}
|
|
2742
|
+
const result = await this.apiClient.insertManyRecords(
|
|
2743
|
+
records,
|
|
2744
|
+
tableInfo.id,
|
|
2745
|
+
options
|
|
2746
|
+
);
|
|
2747
|
+
if (isErrorResponse(result)) {
|
|
2748
|
+
return result;
|
|
2749
|
+
}
|
|
2750
|
+
return result;
|
|
2751
|
+
} catch (error) {
|
|
2752
|
+
return {
|
|
2753
|
+
error: {
|
|
2754
|
+
code: "INSERT_MANY_ERROR",
|
|
2755
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2756
|
+
}
|
|
2757
|
+
};
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
/**
|
|
2761
|
+
* Get a single record by ID
|
|
2762
|
+
*/
|
|
2763
|
+
async get(tableName, recordId) {
|
|
2764
|
+
try {
|
|
2765
|
+
const tableInfo = await this.getTableInfo(tableName);
|
|
2766
|
+
if (!tableInfo) {
|
|
2767
|
+
return {
|
|
2768
|
+
error: {
|
|
2769
|
+
code: "TABLE_NOT_FOUND",
|
|
2770
|
+
message: `Table '${tableName}' not found`
|
|
2771
|
+
}
|
|
2772
|
+
};
|
|
2773
|
+
}
|
|
2774
|
+
const result = await this.apiClient.getRecord(recordId, tableInfo.id);
|
|
2775
|
+
if (isErrorResponse(result)) {
|
|
2776
|
+
return result;
|
|
2777
|
+
}
|
|
2778
|
+
return result;
|
|
2779
|
+
} catch (error) {
|
|
2780
|
+
return {
|
|
2781
|
+
error: {
|
|
2782
|
+
code: "GET_ERROR",
|
|
2783
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2784
|
+
}
|
|
2785
|
+
};
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
/**
|
|
2789
|
+
* List records with filtering and pagination
|
|
2790
|
+
*/
|
|
2791
|
+
async list(tableName, options = {}) {
|
|
2792
|
+
try {
|
|
2793
|
+
const tableInfo = await this.getTableInfo(tableName);
|
|
2794
|
+
if (!tableInfo) {
|
|
2795
|
+
return {
|
|
2796
|
+
error: {
|
|
2797
|
+
code: "TABLE_NOT_FOUND",
|
|
2798
|
+
message: `Table '${tableName}' not found`
|
|
2799
|
+
}
|
|
2800
|
+
};
|
|
2801
|
+
}
|
|
2802
|
+
const requestOptions = { ...options, table_id: tableInfo.id };
|
|
2803
|
+
const result = await this.apiClient.listRecords(requestOptions);
|
|
2804
|
+
if (isErrorResponse(result)) {
|
|
2805
|
+
return result;
|
|
2806
|
+
}
|
|
2807
|
+
return result;
|
|
2808
|
+
} catch (error) {
|
|
2809
|
+
return {
|
|
2810
|
+
error: {
|
|
2811
|
+
code: "LIST_ERROR",
|
|
2812
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2813
|
+
}
|
|
2814
|
+
};
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
/**
|
|
2818
|
+
* Update records by filters
|
|
2819
|
+
*/
|
|
2820
|
+
async update(tableName, options) {
|
|
2821
|
+
try {
|
|
2822
|
+
const tableInfo = await this.getTableInfo(tableName);
|
|
2823
|
+
if (!tableInfo) {
|
|
2824
|
+
return {
|
|
2825
|
+
error: {
|
|
2826
|
+
code: "TABLE_NOT_FOUND",
|
|
2827
|
+
message: `Table '${tableName}' not found`
|
|
2828
|
+
}
|
|
2829
|
+
};
|
|
2830
|
+
}
|
|
2831
|
+
const requestOptions = { ...options, table_id: tableInfo.id };
|
|
2832
|
+
const result = await this.apiClient.updateRecords(requestOptions);
|
|
2833
|
+
if (isErrorResponse(result)) {
|
|
2834
|
+
return result;
|
|
2835
|
+
}
|
|
2836
|
+
return result;
|
|
2837
|
+
} catch (error) {
|
|
2838
|
+
return {
|
|
2839
|
+
error: {
|
|
2840
|
+
code: "UPDATE_ERROR",
|
|
2841
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2842
|
+
}
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
/**
|
|
2847
|
+
* Update a single record by ID
|
|
2848
|
+
*/
|
|
2849
|
+
async updateById(tableName, recordId, data) {
|
|
2850
|
+
try {
|
|
2851
|
+
const tableInfo = await this.getTableInfo(tableName);
|
|
2852
|
+
if (!tableInfo) {
|
|
2853
|
+
return {
|
|
2854
|
+
error: {
|
|
2855
|
+
code: "TABLE_NOT_FOUND",
|
|
2856
|
+
message: `Table '${tableName}' not found`
|
|
2857
|
+
}
|
|
2858
|
+
};
|
|
2859
|
+
}
|
|
2860
|
+
const requestOptions = {
|
|
2861
|
+
id: recordId,
|
|
2862
|
+
set: data,
|
|
2863
|
+
table_id: tableInfo.id
|
|
2864
|
+
};
|
|
2865
|
+
const result = await this.apiClient.updateRecordById(
|
|
2866
|
+
recordId,
|
|
2867
|
+
requestOptions
|
|
2868
|
+
);
|
|
2869
|
+
if (isErrorResponse(result)) {
|
|
2870
|
+
return result;
|
|
2871
|
+
}
|
|
2872
|
+
return result;
|
|
2873
|
+
} catch (error) {
|
|
2874
|
+
return {
|
|
2875
|
+
error: {
|
|
2876
|
+
code: "UPDATE_BY_ID_ERROR",
|
|
2877
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2878
|
+
}
|
|
2879
|
+
};
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
/**
|
|
2883
|
+
* Unified delete method that supports both record IDs and filters
|
|
2884
|
+
*/
|
|
2885
|
+
async delete(tableName, options) {
|
|
2886
|
+
try {
|
|
2887
|
+
const tableInfo = await this.getTableInfo(tableName);
|
|
2888
|
+
if (!tableInfo) {
|
|
2889
|
+
return {
|
|
2890
|
+
error: {
|
|
2891
|
+
code: "TABLE_NOT_FOUND",
|
|
2892
|
+
message: `Table '${tableName}' not found`
|
|
2893
|
+
}
|
|
2894
|
+
};
|
|
2895
|
+
}
|
|
2896
|
+
const requestOptions = { ...options, table_id: tableInfo.id };
|
|
2897
|
+
const result = await this.apiClient.deleteRecords(requestOptions);
|
|
2898
|
+
if (isErrorResponse(result)) {
|
|
2899
|
+
return result;
|
|
2900
|
+
}
|
|
2901
|
+
return result;
|
|
2902
|
+
} catch (error) {
|
|
2903
|
+
return {
|
|
2904
|
+
error: {
|
|
2905
|
+
code: "DELETE_ERROR",
|
|
2906
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2907
|
+
}
|
|
2908
|
+
};
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
/**
|
|
2912
|
+
* Delete a single record by ID
|
|
2913
|
+
*/
|
|
2914
|
+
async deleteById(tableName, recordId) {
|
|
2915
|
+
try {
|
|
2916
|
+
const tableInfo = await this.getTableInfo(tableName);
|
|
2917
|
+
if (!tableInfo) {
|
|
2918
|
+
return {
|
|
2919
|
+
error: {
|
|
2920
|
+
code: "TABLE_NOT_FOUND",
|
|
2921
|
+
message: `Table '${tableName}' not found`
|
|
2922
|
+
}
|
|
2923
|
+
};
|
|
2924
|
+
}
|
|
2925
|
+
const result = await this.apiClient.deleteRecordById(recordId, {
|
|
2926
|
+
table_id: tableInfo.id
|
|
2927
|
+
});
|
|
2928
|
+
if (isErrorResponse(result)) {
|
|
2929
|
+
return result;
|
|
2930
|
+
}
|
|
2931
|
+
return result;
|
|
2932
|
+
} catch (error) {
|
|
2933
|
+
return {
|
|
2934
|
+
error: {
|
|
2935
|
+
code: "DELETE_BY_ID_ERROR",
|
|
2936
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2937
|
+
}
|
|
2938
|
+
};
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
/**
|
|
2942
|
+
* Helper method to get table information by name
|
|
2943
|
+
*/
|
|
2944
|
+
async getTableInfo(tableName) {
|
|
2945
|
+
try {
|
|
2946
|
+
const tableResource = new TableResource(this.client);
|
|
2947
|
+
const tableResult = await tableResource.findByName(tableName);
|
|
2948
|
+
if (tableResult.data) {
|
|
2949
|
+
return {
|
|
2950
|
+
id: tableResult.data.id,
|
|
2951
|
+
snapshot_url: tableResult.data.snapshot_url
|
|
2952
|
+
};
|
|
2953
|
+
}
|
|
2954
|
+
return null;
|
|
2955
|
+
} catch (error) {
|
|
2956
|
+
console.error("Error getting table info:", error);
|
|
2957
|
+
return null;
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2960
|
+
/**
|
|
2961
|
+
* Helper method to ensure all required fields for a record are present,
|
|
2962
|
+
* filling missing ones with null.
|
|
2963
|
+
*/
|
|
2964
|
+
async ensureCompleteRecordData(tableName, data) {
|
|
2965
|
+
try {
|
|
2966
|
+
const columnResource = new ColumnResource(this.client);
|
|
2967
|
+
const columnsResult = await columnResource.findAll(tableName);
|
|
2968
|
+
if (isErrorResponse(columnsResult)) {
|
|
2969
|
+
return columnsResult;
|
|
2970
|
+
}
|
|
2971
|
+
const columns = Array.isArray(columnsResult.data) ? columnsResult.data : [];
|
|
2972
|
+
const completeData = { ...data };
|
|
2973
|
+
for (const column of columns) {
|
|
2974
|
+
if (column.name === "id" || column.name === "created_at" || column.name === "updated_at") {
|
|
2975
|
+
continue;
|
|
2976
|
+
}
|
|
2977
|
+
if (!(column.name in data)) {
|
|
2978
|
+
completeData[column.name] = null;
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
return completeData;
|
|
2982
|
+
} catch (error) {
|
|
2983
|
+
return {
|
|
2984
|
+
error: {
|
|
2985
|
+
code: "COMPLETE_DATA_ERROR",
|
|
2986
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
2987
|
+
}
|
|
2988
|
+
};
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
class RecordBuilder {
|
|
2993
|
+
constructor(options) {
|
|
2994
|
+
this.queryOptions = {};
|
|
2995
|
+
this.updateData = {};
|
|
2996
|
+
this.tableName = options.tableName;
|
|
2997
|
+
this.recordResource = options.recordResource;
|
|
2998
|
+
}
|
|
2999
|
+
/**
|
|
3000
|
+
* Add filter conditions
|
|
3001
|
+
*/
|
|
3002
|
+
where(conditions) {
|
|
3003
|
+
if (!this.queryOptions.filters) {
|
|
3004
|
+
this.queryOptions.filters = [];
|
|
3005
|
+
}
|
|
3006
|
+
Object.entries(conditions).forEach(([field, value]) => {
|
|
3007
|
+
this.queryOptions.filters.push({
|
|
3008
|
+
field,
|
|
3009
|
+
operator: "equals",
|
|
3010
|
+
values: [value]
|
|
3011
|
+
});
|
|
3012
|
+
});
|
|
3013
|
+
return this;
|
|
3014
|
+
}
|
|
3015
|
+
/**
|
|
3016
|
+
* Set sorting
|
|
3017
|
+
*/
|
|
3018
|
+
orderBy(field, direction = "asc") {
|
|
3019
|
+
if (!this.queryOptions.sort) {
|
|
3020
|
+
this.queryOptions.sort = [];
|
|
3021
|
+
}
|
|
3022
|
+
this.queryOptions.sort.push({ field, order: direction });
|
|
3023
|
+
return this;
|
|
3024
|
+
}
|
|
3025
|
+
/**
|
|
3026
|
+
* Set limit (using page)
|
|
3027
|
+
*/
|
|
3028
|
+
limit(count) {
|
|
3029
|
+
if (!this.queryOptions.page) {
|
|
3030
|
+
this.queryOptions.page = { page_no: 1, page_size: count };
|
|
3031
|
+
} else {
|
|
3032
|
+
this.queryOptions.page.page_size = count;
|
|
3033
|
+
}
|
|
3034
|
+
return this;
|
|
3035
|
+
}
|
|
3036
|
+
/**
|
|
3037
|
+
* Set offset (using page)
|
|
3038
|
+
*/
|
|
3039
|
+
offset(count) {
|
|
3040
|
+
if (!this.queryOptions.page) {
|
|
3041
|
+
this.queryOptions.page = {
|
|
3042
|
+
page_no: Math.floor(count / 50) + 1,
|
|
3043
|
+
page_size: 50
|
|
3044
|
+
};
|
|
3045
|
+
} else {
|
|
3046
|
+
const pageSize = this.queryOptions.page.page_size || 50;
|
|
3047
|
+
this.queryOptions.page.page_no = Math.floor(count / pageSize) + 1;
|
|
3048
|
+
}
|
|
3049
|
+
return this;
|
|
3050
|
+
}
|
|
3051
|
+
/**
|
|
3052
|
+
* Set fields to select
|
|
3053
|
+
*/
|
|
3054
|
+
select(fields) {
|
|
3055
|
+
this.queryOptions.fields = fields;
|
|
3056
|
+
return this;
|
|
3057
|
+
}
|
|
3058
|
+
/**
|
|
3059
|
+
* Set data for update operations
|
|
3060
|
+
*/
|
|
3061
|
+
set(data) {
|
|
3062
|
+
this.updateData = { ...this.updateData, ...data };
|
|
3063
|
+
return this;
|
|
3064
|
+
}
|
|
3065
|
+
/**
|
|
3066
|
+
* Set pagination
|
|
3067
|
+
*/
|
|
3068
|
+
page(pageNo, pageSize = 50) {
|
|
3069
|
+
this.queryOptions.page = {
|
|
3070
|
+
page_no: pageNo,
|
|
3071
|
+
page_size: pageSize
|
|
3072
|
+
};
|
|
3073
|
+
return this;
|
|
3074
|
+
}
|
|
3075
|
+
/**
|
|
3076
|
+
* Execute list operation (was findAll)
|
|
3077
|
+
*/
|
|
3078
|
+
async list() {
|
|
3079
|
+
return this.recordResource.list(this.tableName, this.queryOptions);
|
|
3080
|
+
}
|
|
3081
|
+
/**
|
|
3082
|
+
* Execute findAll operation (alias for list)
|
|
3083
|
+
*/
|
|
3084
|
+
async findAll() {
|
|
3085
|
+
return this.recordResource.list(this.tableName, this.queryOptions);
|
|
3086
|
+
}
|
|
3087
|
+
/**
|
|
3088
|
+
* Execute findOne operation by getting first result from list
|
|
3089
|
+
*/
|
|
3090
|
+
async findOne() {
|
|
3091
|
+
const queryOptions = { ...this.queryOptions, limit: 1 };
|
|
3092
|
+
const result = await this.recordResource.list(this.tableName, queryOptions);
|
|
3093
|
+
if ("error" in result) {
|
|
3094
|
+
return result;
|
|
3095
|
+
}
|
|
3096
|
+
const record = result.data.length > 0 ? result.data[0] : null;
|
|
3097
|
+
return {
|
|
3098
|
+
data: record,
|
|
3099
|
+
message: record ? "Record found" : "No record found"
|
|
3100
|
+
};
|
|
3101
|
+
}
|
|
3102
|
+
/**
|
|
3103
|
+
* Build where conditions from filters for API consumption
|
|
3104
|
+
*/
|
|
3105
|
+
buildWhereConditions() {
|
|
3106
|
+
const where = {};
|
|
3107
|
+
if (this.queryOptions.filters) {
|
|
3108
|
+
this.queryOptions.filters.forEach((filter) => {
|
|
3109
|
+
if ("field" in filter && "values" in filter) {
|
|
3110
|
+
const apiFilter = filter;
|
|
3111
|
+
const fieldName = String(apiFilter.field);
|
|
3112
|
+
if (apiFilter.operator === "equals") {
|
|
3113
|
+
where[fieldName] = apiFilter.values[0];
|
|
3114
|
+
} else if (apiFilter.operator === "contains") {
|
|
3115
|
+
where[fieldName] = { $like: `%${String(apiFilter.values[0])}%` };
|
|
3116
|
+
} else {
|
|
3117
|
+
where[fieldName] = apiFilter.values[0];
|
|
3118
|
+
}
|
|
3119
|
+
} else {
|
|
3120
|
+
Object.assign(where, filter);
|
|
3121
|
+
}
|
|
3122
|
+
});
|
|
3123
|
+
}
|
|
3124
|
+
return where;
|
|
3125
|
+
}
|
|
3126
|
+
/**
|
|
3127
|
+
* Execute update operation - requires filters or record IDs
|
|
3128
|
+
*/
|
|
3129
|
+
async update() {
|
|
3130
|
+
if (!this.updateData) {
|
|
3131
|
+
return {
|
|
3132
|
+
error: {
|
|
3133
|
+
code: "MISSING_UPDATE_DATA",
|
|
3134
|
+
message: "Update data is required for update operation"
|
|
3135
|
+
}
|
|
3136
|
+
};
|
|
3137
|
+
}
|
|
3138
|
+
const updateOptions = {
|
|
3139
|
+
set: this.updateData,
|
|
3140
|
+
filters: this.queryOptions.filters || []
|
|
3141
|
+
};
|
|
3142
|
+
return this.recordResource.update(this.tableName, updateOptions);
|
|
3143
|
+
}
|
|
3144
|
+
/**
|
|
3145
|
+
* Execute update by ID operation
|
|
3146
|
+
*/
|
|
3147
|
+
async updateById(id) {
|
|
3148
|
+
return this.recordResource.updateById(this.tableName, id, this.updateData);
|
|
3149
|
+
}
|
|
3150
|
+
/**
|
|
3151
|
+
* Execute delete by single ID operation
|
|
3152
|
+
*/
|
|
3153
|
+
async deleteById(id) {
|
|
3154
|
+
return this.recordResource.deleteById(this.tableName, id);
|
|
3155
|
+
}
|
|
3156
|
+
/**
|
|
3157
|
+
* Execute delete by IDs operation
|
|
3158
|
+
*/
|
|
3159
|
+
async deleteByIds(ids) {
|
|
3160
|
+
return this.recordResource.delete(this.tableName, { record_ids: ids });
|
|
3161
|
+
}
|
|
3162
|
+
/**
|
|
3163
|
+
* Execute delete operation using filters
|
|
3164
|
+
*/
|
|
3165
|
+
async delete() {
|
|
3166
|
+
if (!this.queryOptions.filters || this.queryOptions.filters.length === 0) {
|
|
3167
|
+
return {
|
|
3168
|
+
error: {
|
|
3169
|
+
code: "MISSING_DELETE_CONDITIONS",
|
|
3170
|
+
message: "Filter conditions are required for delete operation. Use where() to specify conditions."
|
|
3171
|
+
}
|
|
3172
|
+
};
|
|
3173
|
+
}
|
|
3174
|
+
const deleteOptions = {
|
|
3175
|
+
filters: this.buildWhereConditions()
|
|
3176
|
+
};
|
|
3177
|
+
return this.recordResource.delete(this.tableName, deleteOptions);
|
|
3178
|
+
}
|
|
3179
|
+
/**
|
|
3180
|
+
* Get the built query options (for debugging)
|
|
3181
|
+
*/
|
|
3182
|
+
getQueryOptions() {
|
|
3183
|
+
return { ...this.queryOptions };
|
|
3184
|
+
}
|
|
3185
|
+
/**
|
|
3186
|
+
* Get the update data (for debugging)
|
|
3187
|
+
*/
|
|
3188
|
+
getUpdateData() {
|
|
3189
|
+
return { ...this.updateData };
|
|
3190
|
+
}
|
|
3191
|
+
/**
|
|
3192
|
+
* Execute insert operation
|
|
3193
|
+
*/
|
|
3194
|
+
async insert(data) {
|
|
3195
|
+
return this.recordResource.insert(this.tableName, data);
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
function createRecordBuilder(options) {
|
|
3199
|
+
return new RecordBuilder(options);
|
|
3200
|
+
}
|
|
3201
|
+
const SQL_ENDPOINTS = {
|
|
3202
|
+
textToSQL: {
|
|
3203
|
+
path: "/tables/query/text-to-sql",
|
|
3204
|
+
method: "POST",
|
|
3205
|
+
authenticated: true,
|
|
3206
|
+
rateLimit: { requests: 100, window: 6e4 }
|
|
3207
|
+
// Limited due to AI processing
|
|
3208
|
+
},
|
|
3209
|
+
executeSQL: {
|
|
3210
|
+
path: "/tables/query/execute",
|
|
3211
|
+
method: "POST",
|
|
3212
|
+
authenticated: true,
|
|
3213
|
+
rateLimit: { requests: 200, window: 6e4 }
|
|
3214
|
+
}
|
|
3215
|
+
};
|
|
3216
|
+
const buildSqlEndpointPath = (endpoint, params = {}) => {
|
|
3217
|
+
let path = endpoint.path;
|
|
3218
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
3219
|
+
path = path.replace(`{${key}}`, encodeURIComponent(value));
|
|
3220
|
+
});
|
|
3221
|
+
return path;
|
|
3222
|
+
};
|
|
3223
|
+
class BaseApiClient {
|
|
3224
|
+
constructor(apiKey, config = {}) {
|
|
3225
|
+
this.config = { apiKey, ...config };
|
|
3226
|
+
this.httpAdapter = createHttpAdapter();
|
|
3227
|
+
const environment = config.environment || "prod";
|
|
3228
|
+
const region = config.region || "asia-south1";
|
|
3229
|
+
this.baseURL = this.getBaseURL(environment, region);
|
|
3230
|
+
}
|
|
3231
|
+
getBaseURL(environment, region) {
|
|
3232
|
+
const regionConfig = REGION_CONFIGS[region];
|
|
3233
|
+
if (!regionConfig) {
|
|
3234
|
+
throw new Error(`Unsupported region: ${region}`);
|
|
3235
|
+
}
|
|
3236
|
+
const envConfig = regionConfig[environment];
|
|
3237
|
+
if (!envConfig) {
|
|
3238
|
+
throw new Error(
|
|
3239
|
+
`Unsupported environment: ${environment} for region: ${region}`
|
|
3240
|
+
);
|
|
3241
|
+
}
|
|
3242
|
+
return `${envConfig.baseURL}/v1`;
|
|
3243
|
+
}
|
|
3244
|
+
buildHeaders() {
|
|
3245
|
+
return {
|
|
3246
|
+
"Content-Type": "application/json",
|
|
3247
|
+
Accept: "application/json",
|
|
3248
|
+
"x-boltic-token": this.config.apiKey,
|
|
3249
|
+
...this.config.headers
|
|
3250
|
+
};
|
|
3251
|
+
}
|
|
3252
|
+
formatErrorResponse(error, prefix = "API") {
|
|
3253
|
+
if (this.config.debug) {
|
|
3254
|
+
console.error(`${prefix} Error:`, error);
|
|
3255
|
+
}
|
|
3256
|
+
if (error && typeof error === "object" && "response" in error) {
|
|
3257
|
+
const apiError = error;
|
|
3258
|
+
if (apiError.response?.data?.error) {
|
|
3259
|
+
return apiError.response.data;
|
|
3260
|
+
}
|
|
3261
|
+
return {
|
|
3262
|
+
error: {
|
|
3263
|
+
code: `${prefix}_ERROR`,
|
|
3264
|
+
message: error.message || `Unknown ${prefix} error`,
|
|
3265
|
+
meta: [`Status: ${apiError.response?.status || "unknown"}`]
|
|
3266
|
+
}
|
|
3267
|
+
};
|
|
3268
|
+
}
|
|
3269
|
+
if (error && typeof error === "object" && "message" in error) {
|
|
3270
|
+
return {
|
|
3271
|
+
error: {
|
|
3272
|
+
code: `${prefix}_CLIENT_ERROR`,
|
|
3273
|
+
message: error.message,
|
|
3274
|
+
meta: [`${prefix} client-side error occurred`]
|
|
3275
|
+
}
|
|
3276
|
+
};
|
|
3277
|
+
}
|
|
3278
|
+
return {
|
|
3279
|
+
error: {
|
|
3280
|
+
code: `${prefix}_UNKNOWN_ERROR`,
|
|
3281
|
+
message: `An unexpected ${prefix} error occurred`,
|
|
3282
|
+
meta: [`Unknown ${prefix} error type`]
|
|
3283
|
+
}
|
|
3284
|
+
};
|
|
3285
|
+
}
|
|
3286
|
+
// Security methods to prevent API key exposure
|
|
3287
|
+
toString() {
|
|
3288
|
+
return `${this.constructor.name} { environment: "${this.config.environment || "prod"}", debug: ${this.config.debug || false} }`;
|
|
3289
|
+
}
|
|
3290
|
+
toJSON() {
|
|
3291
|
+
const safeConfig = { ...this.config };
|
|
3292
|
+
delete safeConfig.apiKey;
|
|
3293
|
+
return {
|
|
3294
|
+
client: this.constructor.name,
|
|
3295
|
+
config: safeConfig
|
|
3296
|
+
};
|
|
3297
|
+
}
|
|
3298
|
+
// Custom inspect method for Node.js console logging
|
|
3299
|
+
[Symbol.for("nodejs.util.inspect.custom")]() {
|
|
3300
|
+
return this.toString();
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
class SqlApiClient extends BaseApiClient {
|
|
3304
|
+
constructor(apiKey, config = {}) {
|
|
3305
|
+
super(apiKey, config);
|
|
3306
|
+
}
|
|
3307
|
+
/**
|
|
3308
|
+
* Convert natural language to SQL query (streaming)
|
|
3309
|
+
*/
|
|
3310
|
+
async textToSQL(request) {
|
|
3311
|
+
try {
|
|
3312
|
+
const endpoint = SQL_ENDPOINTS.textToSQL;
|
|
3313
|
+
const url = `${this.baseURL}${buildSqlEndpointPath(endpoint)}`;
|
|
3314
|
+
const response = await this.httpAdapter.request({
|
|
3315
|
+
url,
|
|
3316
|
+
method: endpoint.method,
|
|
3317
|
+
headers: this.buildHeaders(),
|
|
3318
|
+
data: request,
|
|
3319
|
+
timeout: this.config.timeout
|
|
3320
|
+
});
|
|
3321
|
+
if (response.status >= 400) {
|
|
3322
|
+
return this.formatErrorResponse(
|
|
3323
|
+
{
|
|
3324
|
+
response: { data: response.data, status: response.status }
|
|
3325
|
+
},
|
|
3326
|
+
"SQL"
|
|
3327
|
+
);
|
|
3328
|
+
}
|
|
3329
|
+
const sqlResponse = response.data;
|
|
3330
|
+
return this.createAsyncIterable(sqlResponse.data);
|
|
3331
|
+
} catch (error) {
|
|
3332
|
+
return this.formatErrorResponse(error, "SQL");
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
/**
|
|
3336
|
+
* Execute SQL query
|
|
3337
|
+
*/
|
|
3338
|
+
async executeSQL(request) {
|
|
3339
|
+
try {
|
|
3340
|
+
const endpoint = SQL_ENDPOINTS.executeSQL;
|
|
3341
|
+
const url = `${this.baseURL}${buildSqlEndpointPath(endpoint)}`;
|
|
3342
|
+
const response = await this.httpAdapter.request({
|
|
3343
|
+
url,
|
|
3344
|
+
method: endpoint.method,
|
|
3345
|
+
headers: this.buildHeaders(),
|
|
3346
|
+
data: request,
|
|
3347
|
+
timeout: this.config.timeout
|
|
3348
|
+
});
|
|
3349
|
+
if (response.status >= 400) {
|
|
3350
|
+
return this.formatErrorResponse(
|
|
3351
|
+
{
|
|
3352
|
+
response: { data: response.data, status: response.status }
|
|
3353
|
+
},
|
|
3354
|
+
"SQL"
|
|
3355
|
+
);
|
|
3356
|
+
}
|
|
3357
|
+
return response.data;
|
|
3358
|
+
} catch (error) {
|
|
3359
|
+
return this.formatErrorResponse(error, "SQL");
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
/**
|
|
3363
|
+
* Helper method to create AsyncIterable from string data
|
|
3364
|
+
* TODO: Replace with proper streaming implementation when backend supports it
|
|
3365
|
+
*/
|
|
3366
|
+
async *createAsyncIterable(data) {
|
|
3367
|
+
yield data;
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
function transformTextToSQLRequest(prompt, options = {}) {
|
|
3371
|
+
return {
|
|
3372
|
+
prompt,
|
|
3373
|
+
current_query: options.currentQuery
|
|
3374
|
+
};
|
|
3375
|
+
}
|
|
3376
|
+
class SqlResource {
|
|
3377
|
+
constructor(client) {
|
|
3378
|
+
const config = client.getConfig();
|
|
3379
|
+
this.sqlApiClient = new SqlApiClient(config.apiKey, {
|
|
3380
|
+
environment: config.environment,
|
|
3381
|
+
region: config.region,
|
|
3382
|
+
timeout: config.timeout,
|
|
3383
|
+
debug: config.debug,
|
|
3384
|
+
retryAttempts: config.retryAttempts,
|
|
3385
|
+
retryDelay: config.retryDelay,
|
|
3386
|
+
headers: config.headers
|
|
3387
|
+
});
|
|
3388
|
+
}
|
|
3389
|
+
/**
|
|
3390
|
+
* Convert natural language to SQL query
|
|
3391
|
+
* Returns streaming results for real-time query generation
|
|
3392
|
+
*
|
|
3393
|
+
* @param prompt - Natural language description of the desired query
|
|
3394
|
+
* @param options - Optional parameters including currentQuery for refinement
|
|
3395
|
+
* @returns AsyncIterable<string> for streaming SQL generation
|
|
3396
|
+
*
|
|
3397
|
+
*/
|
|
3398
|
+
async textToSQL(prompt, options = {}) {
|
|
3399
|
+
const request = transformTextToSQLRequest(prompt, options);
|
|
3400
|
+
const response = await this.sqlApiClient.textToSQL(request);
|
|
3401
|
+
if ("error" in response && response.error !== void 0) {
|
|
3402
|
+
throw response;
|
|
3403
|
+
}
|
|
3404
|
+
return response;
|
|
3405
|
+
}
|
|
3406
|
+
/**
|
|
3407
|
+
* Execute SQL query with built-in safety measures and performance optimization
|
|
3408
|
+
*
|
|
3409
|
+
* @param query - SQL query string to execute
|
|
3410
|
+
* @returns Promise<ExecuteSQLApiResponse> with raw API response following Boltic API Response Structure
|
|
3411
|
+
*
|
|
3412
|
+
*/
|
|
3413
|
+
async executeSQL(query) {
|
|
3414
|
+
const response = await this.sqlApiClient.executeSQL({ query });
|
|
3415
|
+
if (isErrorResponse(response)) {
|
|
3416
|
+
return response;
|
|
3417
|
+
}
|
|
3418
|
+
return response;
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
class TableBuilder {
|
|
3422
|
+
constructor(options, tablesApiClient) {
|
|
3423
|
+
this.isPublic = false;
|
|
3424
|
+
this.fields = [];
|
|
3425
|
+
this.tableName = options.name;
|
|
3426
|
+
this.description = options.description;
|
|
3427
|
+
this.tablesApiClient = tablesApiClient;
|
|
3428
|
+
}
|
|
3429
|
+
/**
|
|
3430
|
+
* Set table name
|
|
3431
|
+
*/
|
|
3432
|
+
name(name) {
|
|
3433
|
+
this.tableName = name;
|
|
3434
|
+
return this;
|
|
3435
|
+
}
|
|
3436
|
+
/**
|
|
3437
|
+
* Set table description
|
|
3438
|
+
*/
|
|
3439
|
+
describe(description) {
|
|
3440
|
+
this.description = description;
|
|
3441
|
+
return this;
|
|
3442
|
+
}
|
|
3443
|
+
/**
|
|
3444
|
+
* Set if table is public
|
|
3445
|
+
*/
|
|
3446
|
+
public(isPublic = true) {
|
|
3447
|
+
this.isPublic = isPublic;
|
|
3448
|
+
return this;
|
|
3449
|
+
}
|
|
3450
|
+
/**
|
|
3451
|
+
* Add a text field
|
|
3452
|
+
*/
|
|
3453
|
+
text(name, options = {}) {
|
|
3454
|
+
this.fields.push({
|
|
3455
|
+
name,
|
|
3456
|
+
type: "text",
|
|
3457
|
+
is_nullable: options.nullable ?? true,
|
|
3458
|
+
is_unique: options.unique ?? false,
|
|
3459
|
+
is_indexed: options.indexed ?? false,
|
|
3460
|
+
is_primary_key: false,
|
|
3461
|
+
default_value: options.defaultValue,
|
|
3462
|
+
description: options.description,
|
|
3463
|
+
alignment: options.alignment || "left",
|
|
3464
|
+
field_order: this.fields.length + 1
|
|
3465
|
+
});
|
|
3466
|
+
return this;
|
|
3467
|
+
}
|
|
3468
|
+
/**
|
|
3469
|
+
* Add a long text field
|
|
3470
|
+
*/
|
|
3471
|
+
longText(name, options = {}) {
|
|
3472
|
+
this.fields.push({
|
|
3473
|
+
name,
|
|
3474
|
+
type: "long-text",
|
|
3475
|
+
is_nullable: options.nullable ?? true,
|
|
3476
|
+
is_unique: false,
|
|
3477
|
+
is_indexed: false,
|
|
3478
|
+
is_primary_key: false,
|
|
3479
|
+
description: options.description,
|
|
3480
|
+
alignment: options.alignment || "left",
|
|
3481
|
+
field_order: this.fields.length + 1
|
|
3482
|
+
});
|
|
3483
|
+
return this;
|
|
3484
|
+
}
|
|
3485
|
+
/**
|
|
3486
|
+
* Add a number field
|
|
3487
|
+
*/
|
|
3488
|
+
number(name, options = {}) {
|
|
3489
|
+
this.fields.push({
|
|
3490
|
+
name,
|
|
3491
|
+
type: "number",
|
|
3492
|
+
is_nullable: options.nullable ?? true,
|
|
3493
|
+
is_unique: options.unique ?? false,
|
|
3494
|
+
is_indexed: options.indexed ?? false,
|
|
3495
|
+
is_primary_key: false,
|
|
3496
|
+
default_value: options.defaultValue,
|
|
3497
|
+
description: options.description,
|
|
3498
|
+
decimals: options.decimals,
|
|
3499
|
+
alignment: options.alignment || "right",
|
|
3500
|
+
field_order: this.fields.length + 1
|
|
3501
|
+
});
|
|
3502
|
+
return this;
|
|
3503
|
+
}
|
|
3504
|
+
/**
|
|
3505
|
+
* Add a currency field
|
|
3506
|
+
*/
|
|
3507
|
+
currency(name, options = {}) {
|
|
3508
|
+
this.fields.push({
|
|
3509
|
+
name,
|
|
3510
|
+
type: "currency",
|
|
3511
|
+
is_nullable: options.nullable ?? true,
|
|
3512
|
+
is_unique: false,
|
|
3513
|
+
is_indexed: false,
|
|
3514
|
+
is_primary_key: false,
|
|
3515
|
+
default_value: options.defaultValue,
|
|
3516
|
+
description: options.description,
|
|
3517
|
+
currency_format: options.currencyFormat,
|
|
3518
|
+
decimals: options.decimals,
|
|
3519
|
+
alignment: "right",
|
|
3520
|
+
field_order: this.fields.length + 1
|
|
3521
|
+
});
|
|
3522
|
+
return this;
|
|
3523
|
+
}
|
|
3524
|
+
/**
|
|
3525
|
+
* Add a checkbox field
|
|
3526
|
+
*/
|
|
3527
|
+
checkbox(name, options = {}) {
|
|
3528
|
+
this.fields.push({
|
|
3529
|
+
name,
|
|
3530
|
+
type: "checkbox",
|
|
3531
|
+
is_nullable: options.nullable ?? true,
|
|
3532
|
+
is_unique: false,
|
|
3533
|
+
is_indexed: false,
|
|
3534
|
+
is_primary_key: false,
|
|
3535
|
+
default_value: options.defaultValue,
|
|
3536
|
+
description: options.description,
|
|
3537
|
+
alignment: "center",
|
|
3538
|
+
field_order: this.fields.length + 1
|
|
3539
|
+
});
|
|
3540
|
+
return this;
|
|
3541
|
+
}
|
|
3542
|
+
/**
|
|
3543
|
+
* Add a dropdown field
|
|
3544
|
+
*/
|
|
3545
|
+
dropdown(name, items, options = {}) {
|
|
3546
|
+
this.fields.push({
|
|
3547
|
+
name,
|
|
3548
|
+
type: "dropdown",
|
|
3549
|
+
is_nullable: options.nullable ?? true,
|
|
3550
|
+
is_unique: false,
|
|
3551
|
+
is_indexed: false,
|
|
3552
|
+
is_primary_key: false,
|
|
3553
|
+
default_value: options.defaultValue,
|
|
3554
|
+
description: options.description,
|
|
3555
|
+
selection_source: "provide-static-list",
|
|
3556
|
+
selectable_items: items,
|
|
3557
|
+
multiple_selections: options.multiple ?? false,
|
|
3558
|
+
alignment: "left",
|
|
3559
|
+
field_order: this.fields.length + 1
|
|
3560
|
+
});
|
|
3561
|
+
return this;
|
|
3562
|
+
}
|
|
3563
|
+
/**
|
|
3564
|
+
* Add an email field
|
|
3565
|
+
*/
|
|
3566
|
+
email(name, options = {}) {
|
|
3567
|
+
this.fields.push({
|
|
3568
|
+
name,
|
|
3569
|
+
type: "email",
|
|
3570
|
+
is_nullable: options.nullable ?? true,
|
|
3571
|
+
is_unique: options.unique ?? false,
|
|
3572
|
+
is_indexed: options.indexed ?? false,
|
|
3573
|
+
is_primary_key: false,
|
|
3574
|
+
description: options.description,
|
|
3575
|
+
alignment: "left",
|
|
3576
|
+
field_order: this.fields.length + 1
|
|
3577
|
+
});
|
|
3578
|
+
return this;
|
|
3579
|
+
}
|
|
3580
|
+
/**
|
|
3581
|
+
* Add a phone number field
|
|
3582
|
+
*/
|
|
3583
|
+
phone(name, options = {}) {
|
|
3584
|
+
this.fields.push({
|
|
3585
|
+
name,
|
|
3586
|
+
type: "phone-number",
|
|
3587
|
+
is_nullable: options.nullable ?? true,
|
|
3588
|
+
is_unique: false,
|
|
3589
|
+
is_indexed: false,
|
|
3590
|
+
is_primary_key: false,
|
|
3591
|
+
description: options.description,
|
|
3592
|
+
phone_format: options.format,
|
|
3593
|
+
alignment: "left",
|
|
3594
|
+
field_order: this.fields.length + 1
|
|
3595
|
+
});
|
|
3596
|
+
return this;
|
|
3597
|
+
}
|
|
3598
|
+
/**
|
|
3599
|
+
* Add a link field
|
|
3600
|
+
*/
|
|
3601
|
+
link(name, options = {}) {
|
|
3602
|
+
this.fields.push({
|
|
3603
|
+
name,
|
|
3604
|
+
type: "link",
|
|
3605
|
+
is_nullable: options.nullable ?? true,
|
|
3606
|
+
is_unique: false,
|
|
3607
|
+
is_indexed: false,
|
|
3608
|
+
is_primary_key: false,
|
|
3609
|
+
description: options.description,
|
|
3610
|
+
alignment: "left",
|
|
3611
|
+
field_order: this.fields.length + 1
|
|
3612
|
+
});
|
|
3613
|
+
return this;
|
|
3614
|
+
}
|
|
3615
|
+
/**
|
|
3616
|
+
* Add a JSON field
|
|
3617
|
+
*/
|
|
3618
|
+
json(name, options = {}) {
|
|
3619
|
+
this.fields.push({
|
|
3620
|
+
name,
|
|
3621
|
+
type: "json",
|
|
3622
|
+
is_nullable: options.nullable ?? true,
|
|
3623
|
+
is_unique: false,
|
|
3624
|
+
is_indexed: false,
|
|
3625
|
+
is_primary_key: false,
|
|
3626
|
+
description: options.description,
|
|
3627
|
+
alignment: "left",
|
|
3628
|
+
field_order: this.fields.length + 1
|
|
3629
|
+
});
|
|
3630
|
+
return this;
|
|
3631
|
+
}
|
|
3632
|
+
/**
|
|
3633
|
+
* Add a date-time field
|
|
3634
|
+
*/
|
|
3635
|
+
dateTime(name, options = {}) {
|
|
3636
|
+
this.fields.push({
|
|
3637
|
+
name,
|
|
3638
|
+
type: "date-time",
|
|
3639
|
+
is_nullable: options.nullable ?? true,
|
|
3640
|
+
is_unique: false,
|
|
3641
|
+
is_indexed: false,
|
|
3642
|
+
is_primary_key: false,
|
|
3643
|
+
description: options.description,
|
|
3644
|
+
date_format: options.dateFormat,
|
|
3645
|
+
time_format: options.timeFormat,
|
|
3646
|
+
timezone: options.timezone,
|
|
3647
|
+
alignment: "left",
|
|
3648
|
+
field_order: this.fields.length + 1
|
|
3649
|
+
});
|
|
3650
|
+
return this;
|
|
3651
|
+
}
|
|
3652
|
+
/**
|
|
3653
|
+
* Add a vector field
|
|
3654
|
+
*/
|
|
3655
|
+
vector(name, dimension, options = {}) {
|
|
3656
|
+
this.fields.push({
|
|
3657
|
+
name,
|
|
3658
|
+
type: "vector",
|
|
3659
|
+
is_nullable: options.nullable ?? true,
|
|
3660
|
+
is_unique: false,
|
|
3661
|
+
is_indexed: false,
|
|
3662
|
+
is_primary_key: false,
|
|
3663
|
+
description: options.description,
|
|
3664
|
+
vector_dimension: dimension,
|
|
3665
|
+
alignment: "left",
|
|
3666
|
+
field_order: this.fields.length + 1
|
|
3667
|
+
});
|
|
3668
|
+
return this;
|
|
3669
|
+
}
|
|
3670
|
+
/**
|
|
3671
|
+
* Add a custom field
|
|
3672
|
+
*/
|
|
3673
|
+
addField(field) {
|
|
3674
|
+
this.fields.push({
|
|
3675
|
+
...field,
|
|
3676
|
+
field_order: field.field_order || this.fields.length + 1
|
|
3677
|
+
});
|
|
3678
|
+
return this;
|
|
3679
|
+
}
|
|
3680
|
+
/**
|
|
3681
|
+
* Remove a field by name
|
|
3682
|
+
*/
|
|
3683
|
+
removeField(name) {
|
|
3684
|
+
this.fields = this.fields.filter((field) => field.name !== name);
|
|
3685
|
+
this.fields.forEach((field, index) => {
|
|
3686
|
+
field.field_order = index + 1;
|
|
3687
|
+
});
|
|
3688
|
+
return this;
|
|
3689
|
+
}
|
|
3690
|
+
/**
|
|
3691
|
+
* Get current fields
|
|
3692
|
+
*/
|
|
3693
|
+
getFields() {
|
|
3694
|
+
return [...this.fields];
|
|
3695
|
+
}
|
|
3696
|
+
/**
|
|
3697
|
+
* Get current table name
|
|
3698
|
+
*/
|
|
3699
|
+
getName() {
|
|
3700
|
+
return this.tableName;
|
|
3701
|
+
}
|
|
3702
|
+
/**
|
|
3703
|
+
* Get current description
|
|
3704
|
+
*/
|
|
3705
|
+
getDescription() {
|
|
3706
|
+
return this.description;
|
|
3707
|
+
}
|
|
3708
|
+
/**
|
|
3709
|
+
* Build the table request object
|
|
3710
|
+
*/
|
|
3711
|
+
build() {
|
|
3712
|
+
if (!this.tableName) {
|
|
3713
|
+
throw new ValidationError("Table name is required", [
|
|
3714
|
+
{ field: "name", message: "Table name cannot be empty" }
|
|
3715
|
+
]);
|
|
3716
|
+
}
|
|
3717
|
+
if (this.fields.length === 0) {
|
|
3718
|
+
throw new ValidationError("At least one field is required", [
|
|
3719
|
+
{ field: "fields", message: "Table must have at least one field" }
|
|
3720
|
+
]);
|
|
3721
|
+
}
|
|
3722
|
+
return {
|
|
3723
|
+
name: this.tableName,
|
|
3724
|
+
description: this.description,
|
|
3725
|
+
fields: this.fields
|
|
3726
|
+
};
|
|
3727
|
+
}
|
|
3728
|
+
/**
|
|
3729
|
+
* Build and create the table (requires API client)
|
|
3730
|
+
*/
|
|
3731
|
+
async create(options = {}) {
|
|
3732
|
+
if (!this.tablesApiClient) {
|
|
3733
|
+
throw new Error("TablesApiClient is required for table creation");
|
|
3734
|
+
}
|
|
3735
|
+
const request = this.build();
|
|
3736
|
+
return this.tablesApiClient.createTable(request, options);
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
function createTableBuilder(options, tablesApiClient) {
|
|
3740
|
+
return new TableBuilder(options, tablesApiClient);
|
|
3741
|
+
}
|
|
3742
|
+
class BolticClient {
|
|
3743
|
+
constructor(apiKey, options = {}) {
|
|
3744
|
+
this.currentDatabase = null;
|
|
3745
|
+
this.clientOptions = options;
|
|
3746
|
+
this.configManager = new ConfigManager(
|
|
3747
|
+
apiKey,
|
|
3748
|
+
options.environment || "prod",
|
|
3749
|
+
options.region || "asia-south1",
|
|
3750
|
+
options
|
|
3751
|
+
);
|
|
3752
|
+
const config = this.configManager.getConfig();
|
|
3753
|
+
this.authManager = new AuthManager2({
|
|
3754
|
+
apiKey: config.apiKey,
|
|
3755
|
+
maxRetries: config.maxRetries
|
|
3756
|
+
});
|
|
3757
|
+
this.baseClient = new BaseClient(config, this.authManager);
|
|
3758
|
+
this.tableResource = new TableResource(this.baseClient);
|
|
3759
|
+
this.columnResource = new ColumnResource(this.baseClient);
|
|
3760
|
+
this.recordResource = new RecordResource(this.baseClient);
|
|
3761
|
+
this.sqlResource = new SqlResource(this.baseClient);
|
|
3762
|
+
this.currentDatabase = {
|
|
3763
|
+
databaseName: "Default"
|
|
3764
|
+
};
|
|
3765
|
+
}
|
|
3766
|
+
getCurrentDatabase() {
|
|
3767
|
+
return this.currentDatabase;
|
|
3768
|
+
}
|
|
3769
|
+
// Direct table operations
|
|
3770
|
+
get tables() {
|
|
3771
|
+
return {
|
|
3772
|
+
create: (data) => this.tableResource.create(data),
|
|
3773
|
+
findAll: (options) => this.tableResource.findAll(options),
|
|
3774
|
+
findById: (id) => this.tableResource.findById(id),
|
|
3775
|
+
findByName: (name) => this.tableResource.findByName(name),
|
|
3776
|
+
findOne: (options) => this.tableResource.findOne(options),
|
|
3777
|
+
update: (name, data) => this.tableResource.update(name, data),
|
|
3778
|
+
delete: (name) => this.tableResource.delete(name),
|
|
3779
|
+
rename: (oldName, newName) => this.tableResource.rename(oldName, newName),
|
|
3780
|
+
setAccess: (request) => this.tableResource.setAccess(request)
|
|
3781
|
+
};
|
|
3782
|
+
}
|
|
3783
|
+
// Direct column operations
|
|
3784
|
+
get columns() {
|
|
3785
|
+
return {
|
|
3786
|
+
create: (tableName, column) => this.columnResource.create(tableName, column),
|
|
3787
|
+
createMany: (tableName, columns) => this.columnResource.createMany(tableName, columns),
|
|
3788
|
+
findAll: (tableName, options) => this.columnResource.findAll(tableName, options),
|
|
3789
|
+
findOne: (tableName, columnName) => this.columnResource.get(tableName, columnName),
|
|
3790
|
+
findById: (tableName, columnId) => this.columnResource.findById(tableName, columnId),
|
|
3791
|
+
update: (tableName, columnName, updates) => this.columnResource.update(tableName, columnName, updates),
|
|
3792
|
+
delete: (tableName, columnName) => this.columnResource.delete(tableName, columnName)
|
|
3793
|
+
};
|
|
3794
|
+
}
|
|
3795
|
+
// Fluent table operations
|
|
3796
|
+
table(name) {
|
|
3797
|
+
const tableBuilder = createTableBuilder({ name });
|
|
3798
|
+
return tableBuilder;
|
|
3799
|
+
}
|
|
3800
|
+
// Method 3: Table-scoped operations
|
|
3801
|
+
from(tableName) {
|
|
3802
|
+
return {
|
|
3803
|
+
// Column operations for this table
|
|
3804
|
+
columns: () => ({
|
|
3805
|
+
create: (column) => this.columnResource.create(tableName, column),
|
|
3806
|
+
findAll: (options) => this.columnResource.findAll(tableName, options),
|
|
3807
|
+
get: (columnName) => this.columnResource.get(tableName, columnName),
|
|
3808
|
+
update: (columnName, updates) => this.columnResource.update(tableName, columnName, updates),
|
|
3809
|
+
delete: (columnName) => this.columnResource.delete(tableName, columnName)
|
|
3810
|
+
}),
|
|
3811
|
+
// Record operations for this table
|
|
3812
|
+
records: () => ({
|
|
3813
|
+
insert: (data) => this.recordResource.insert(tableName, data),
|
|
3814
|
+
insertMany: (records, options) => this.recordResource.insertMany(tableName, records, options),
|
|
3815
|
+
findOne: (recordId) => this.recordResource.get(tableName, recordId),
|
|
3816
|
+
update: (options) => this.recordResource.update(tableName, options),
|
|
3817
|
+
updateById: (recordId, data) => this.recordResource.updateById(tableName, recordId, data),
|
|
3818
|
+
// Unified delete method
|
|
3819
|
+
delete: (options) => this.recordResource.delete(tableName, options),
|
|
3820
|
+
// Single record delete method
|
|
3821
|
+
deleteById: (recordId) => this.recordResource.deleteById(tableName, recordId)
|
|
3822
|
+
}),
|
|
3823
|
+
// Fluent record builder for this table
|
|
3824
|
+
record: () => createRecordBuilder({
|
|
3825
|
+
tableName,
|
|
3826
|
+
recordResource: this.recordResource
|
|
3827
|
+
})
|
|
3828
|
+
};
|
|
3829
|
+
}
|
|
3830
|
+
// Direct record operations
|
|
3831
|
+
get records() {
|
|
3832
|
+
return {
|
|
3833
|
+
insert: (tableName, data) => this.recordResource.insert(tableName, data),
|
|
3834
|
+
insertMany: (tableName, records, options) => this.recordResource.insertMany(tableName, records, options),
|
|
3835
|
+
findAll: (tableName, options) => this.recordResource.list(tableName, options),
|
|
3836
|
+
findOne: (tableName, recordId) => this.recordResource.get(tableName, recordId),
|
|
3837
|
+
update: (tableName, options) => this.recordResource.update(tableName, options),
|
|
3838
|
+
updateById: (tableName, recordId, data) => this.recordResource.updateById(tableName, recordId, data),
|
|
3839
|
+
delete: (tableName, options) => this.recordResource.delete(tableName, options),
|
|
3840
|
+
deleteById: (tableName, recordId) => this.recordResource.deleteById(tableName, recordId)
|
|
3841
|
+
};
|
|
3842
|
+
}
|
|
3843
|
+
// Method 4: Create fluent record builder
|
|
3844
|
+
record(tableName) {
|
|
3845
|
+
return createRecordBuilder({
|
|
3846
|
+
tableName,
|
|
3847
|
+
recordResource: this.recordResource
|
|
3848
|
+
});
|
|
3849
|
+
}
|
|
3850
|
+
// Direct SQL operations
|
|
3851
|
+
get sql() {
|
|
3852
|
+
return {
|
|
3853
|
+
textToSQL: (prompt, options) => this.sqlResource.textToSQL(prompt, options),
|
|
3854
|
+
executeSQL: (query) => this.sqlResource.executeSQL(query)
|
|
3855
|
+
};
|
|
3856
|
+
}
|
|
3857
|
+
// SQL resource access for testing
|
|
3858
|
+
getSqlResource() {
|
|
3859
|
+
return this.sqlResource;
|
|
3860
|
+
}
|
|
3861
|
+
// Configuration management
|
|
3862
|
+
updateApiKey(newApiKey) {
|
|
3863
|
+
this.configManager.updateConfig({ apiKey: newApiKey });
|
|
3864
|
+
this.authManager.updateApiKey(newApiKey);
|
|
3865
|
+
}
|
|
3866
|
+
updateConfig(updates) {
|
|
3867
|
+
this.configManager.updateConfig(updates);
|
|
3868
|
+
this.baseClient.updateConfig(this.configManager.getConfig());
|
|
3869
|
+
this.updateAllResourcesConfig();
|
|
3870
|
+
}
|
|
3871
|
+
getConfig() {
|
|
3872
|
+
return this.configManager.getConfig();
|
|
3873
|
+
}
|
|
3874
|
+
// Authentication management
|
|
3875
|
+
async validateApiKey() {
|
|
3876
|
+
return this.authManager.validateApiKeyAsync();
|
|
3877
|
+
}
|
|
3878
|
+
isAuthenticated() {
|
|
3879
|
+
return this.authManager.isAuthenticated();
|
|
3880
|
+
}
|
|
3881
|
+
// HTTP client access
|
|
3882
|
+
getHttpClient() {
|
|
3883
|
+
return this.baseClient;
|
|
3884
|
+
}
|
|
3885
|
+
// Interceptor management
|
|
3886
|
+
addRequestInterceptor(interceptor) {
|
|
3887
|
+
return this.baseClient.getInterceptors().request.use(interceptor);
|
|
3888
|
+
}
|
|
3889
|
+
addResponseInterceptor(onFulfilled, onRejected) {
|
|
3890
|
+
return this.baseClient.getInterceptors().response.use(onFulfilled, onRejected);
|
|
3891
|
+
}
|
|
3892
|
+
ejectRequestInterceptor(id) {
|
|
3893
|
+
this.baseClient.getInterceptors().request.eject(id);
|
|
3894
|
+
}
|
|
3895
|
+
ejectResponseInterceptor(id) {
|
|
3896
|
+
this.baseClient.getInterceptors().response.eject(id);
|
|
3897
|
+
}
|
|
3898
|
+
// Connection testing
|
|
3899
|
+
async testConnection() {
|
|
3900
|
+
try {
|
|
3901
|
+
return await this.authManager.validateApiKeyAsync();
|
|
3902
|
+
} catch (error) {
|
|
3903
|
+
return false;
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
// Get client version
|
|
3907
|
+
getVersion() {
|
|
3908
|
+
return "1.0.0";
|
|
3909
|
+
}
|
|
3910
|
+
// Environment helpers
|
|
3911
|
+
getEnvironment() {
|
|
3912
|
+
return this.configManager.getConfig().environment;
|
|
3913
|
+
}
|
|
3914
|
+
getRegion() {
|
|
3915
|
+
return this.configManager.getConfig().region;
|
|
3916
|
+
}
|
|
3917
|
+
// Debug helpers
|
|
3918
|
+
enableDebug() {
|
|
3919
|
+
this.configManager.updateConfig({ debug: true });
|
|
3920
|
+
this.baseClient.updateConfig(this.configManager.getConfig());
|
|
3921
|
+
this.updateAllResourcesConfig();
|
|
3922
|
+
}
|
|
3923
|
+
disableDebug() {
|
|
3924
|
+
this.configManager.updateConfig({ debug: false });
|
|
3925
|
+
this.baseClient.updateConfig(this.configManager.getConfig());
|
|
3926
|
+
this.updateAllResourcesConfig();
|
|
3927
|
+
}
|
|
3928
|
+
isDebugEnabled() {
|
|
3929
|
+
return this.configManager.getConfig().debug || false;
|
|
3930
|
+
}
|
|
3931
|
+
// Private method to update all resource configurations
|
|
3932
|
+
updateAllResourcesConfig() {
|
|
3933
|
+
this.tableResource = new TableResource(this.baseClient);
|
|
3934
|
+
this.columnResource = new ColumnResource(this.baseClient);
|
|
3935
|
+
this.recordResource = new RecordResource(this.baseClient);
|
|
3936
|
+
this.sqlResource = new SqlResource(this.baseClient);
|
|
3937
|
+
}
|
|
3938
|
+
// Security methods to prevent API key exposure
|
|
3939
|
+
toString() {
|
|
3940
|
+
const config = this.getConfig();
|
|
3941
|
+
return `BolticClient { environment: "${config.environment}", region: "${config.region}", debug: ${config.debug} }`;
|
|
3942
|
+
}
|
|
3943
|
+
toJSON() {
|
|
3944
|
+
const config = this.getConfig();
|
|
3945
|
+
return {
|
|
3946
|
+
environment: config.environment,
|
|
3947
|
+
region: config.region,
|
|
3948
|
+
debug: config.debug,
|
|
3949
|
+
timeout: config.timeout,
|
|
3950
|
+
version: this.getVersion()
|
|
3951
|
+
};
|
|
3952
|
+
}
|
|
3953
|
+
// Custom inspect method for Node.js console logging
|
|
3954
|
+
[Symbol.for("nodejs.util.inspect.custom")]() {
|
|
3955
|
+
return this.toString();
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
function createClient(apiKey, options = {}) {
|
|
3959
|
+
return new BolticClient(apiKey, options);
|
|
3960
|
+
}
|
|
3961
|
+
const VERSION = "1.0.0";
|
|
3962
|
+
exports.AuthManager = AuthManager$1;
|
|
3963
|
+
exports.BolticClient = BolticClient;
|
|
3964
|
+
exports.VERSION = VERSION;
|
|
3965
|
+
exports.createClient = createClient;
|
|
3966
|
+
exports.createErrorWithContext = createErrorWithContext$1;
|
|
3967
|
+
exports.formatError = formatError$1;
|
|
3968
|
+
exports.getHttpStatusCode = getHttpStatusCode$1;
|
|
3969
|
+
exports.isErrorResponse = isErrorResponse;
|
|
3970
|
+
exports.isListResponse = isListResponse;
|
|
3971
|
+
exports.isNetworkError = isNetworkError;
|
|
3972
|
+
//# sourceMappingURL=sdk.js.map
|