farseer-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +741 -0
- package/dist/commands/app.d.ts +2 -0
- package/dist/commands/app.js +349 -0
- package/dist/commands/app.js.map +7 -0
- package/dist/commands/apps.d.ts +2 -0
- package/dist/commands/apps.js +111 -0
- package/dist/commands/apps.js.map +7 -0
- package/dist/commands/checkout.d.ts +2 -0
- package/dist/commands/checkout.js +166 -0
- package/dist/commands/checkout.js.map +7 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +139 -0
- package/dist/commands/config.js.map +7 -0
- package/dist/commands/diff.d.ts +2 -0
- package/dist/commands/diff.js +183 -0
- package/dist/commands/diff.js.map +7 -0
- package/dist/commands/files.js +99 -0
- package/dist/commands/files.js.map +7 -0
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.js +79 -0
- package/dist/commands/install.js.map +7 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.js +92 -0
- package/dist/commands/list.js.map +7 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +134 -0
- package/dist/commands/login.js.map +7 -0
- package/dist/commands/logout.d.ts +2 -0
- package/dist/commands/logout.js +59 -0
- package/dist/commands/logout.js.map +7 -0
- package/dist/commands/mcp-server.d.ts +8 -0
- package/dist/commands/mcp-server.js +41 -0
- package/dist/commands/mcp-server.js.map +7 -0
- package/dist/commands/model.d.ts +2 -0
- package/dist/commands/model.js +189 -0
- package/dist/commands/model.js.map +7 -0
- package/dist/commands/pull.d.ts +2 -0
- package/dist/commands/pull.js +287 -0
- package/dist/commands/pull.js.map +7 -0
- package/dist/commands/push.d.ts +2 -0
- package/dist/commands/push.js +251 -0
- package/dist/commands/push.js.map +7 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +246 -0
- package/dist/commands/run.js.map +7 -0
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +137 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +145 -0
- package/dist/commands/status.js.map +7 -0
- package/dist/commands/unsetup.d.ts +2 -0
- package/dist/commands/unsetup.js +122 -0
- package/dist/commands/whoami.d.ts +2 -0
- package/dist/commands/whoami.js +63 -0
- package/dist/commands/whoami.js.map +7 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +135 -0
- package/dist/index.js.map +7 -0
- package/dist/mcp/index.d.ts +7 -0
- package/dist/mcp/index.js +35 -0
- package/dist/mcp/index.js.map +7 -0
- package/dist/mcp/prompts/workflows.d.ts +7 -0
- package/dist/mcp/prompts/workflows.js +374 -0
- package/dist/mcp/prompts/workflows.js.map +7 -0
- package/dist/mcp/resources/documentation.d.ts +8 -0
- package/dist/mcp/resources/documentation.js +167 -0
- package/dist/mcp/resources/documentation.js.map +7 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +49 -0
- package/dist/mcp/server.js.map +7 -0
- package/dist/mcp/tools/appTools.d.ts +7 -0
- package/dist/mcp/tools/appTools.js +377 -0
- package/dist/mcp/tools/appTools.js.map +7 -0
- package/dist/mcp/tools/authTools.d.ts +7 -0
- package/dist/mcp/tools/authTools.js +158 -0
- package/dist/mcp/tools/authTools.js.map +7 -0
- package/dist/mcp/tools/modelTools.d.ts +7 -0
- package/dist/mcp/tools/modelTools.js +331 -0
- package/dist/mcp/tools/modelTools.js.map +7 -0
- package/dist/mcp/tools/runTools.d.ts +7 -0
- package/dist/mcp/tools/runTools.js +231 -0
- package/dist/mcp/tools/runTools.js.map +7 -0
- package/dist/mcp/tools/syncTools.d.ts +7 -0
- package/dist/mcp/tools/syncTools.js +382 -0
- package/dist/mcp/tools/syncTools.js.map +7 -0
- package/dist/mcp/utils/helpers.d.ts +69 -0
- package/dist/mcp/utils/helpers.js +113 -0
- package/dist/mcp/utils/helpers.js.map +7 -0
- package/dist/services/appSyncService.d.ts +75 -0
- package/dist/services/appSyncService.js +370 -0
- package/dist/services/appSyncService.js.map +7 -0
- package/dist/services/configService.d.ts +39 -0
- package/dist/services/configService.js +196 -0
- package/dist/services/configService.js.map +7 -0
- package/dist/services/farseerApi.d.ts +166 -0
- package/dist/services/farseerApi.js +378 -0
- package/dist/services/farseerApi.js.map +7 -0
- package/dist/services/farseerFactory.d.ts +88 -0
- package/dist/services/farseerFactory.js +179 -0
- package/dist/services/farseerFactory.js.map +7 -0
- package/dist/services/farseerService.d.ts +96 -0
- package/dist/services/farseerService.js +614 -0
- package/dist/services/farseerService.js.map +7 -0
- package/dist/services/gitService.d.ts +31 -0
- package/dist/services/gitService.js +134 -0
- package/dist/services/gitService.js.map +7 -0
- package/dist/services/syncService.d.ts +44 -0
- package/dist/services/syncService.js +320 -0
- package/dist/services/syncService.js.map +7 -0
- package/dist/utils/constants.d.ts +7 -0
- package/dist/utils/constants.js +46 -0
- package/dist/utils/constants.js.map +7 -0
- package/dist/utils/helpers.d.ts +69 -0
- package/dist/utils/helpers.js +413 -0
- package/dist/utils/helpers.js.map +7 -0
- package/dist/utils/logger.d.ts +14 -0
- package/dist/utils/logger.js +76 -0
- package/dist/utils/logger.js.map +7 -0
- package/package.json +62 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/services/configService.ts"],
|
|
4
|
+
"sourcesContent": ["import * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\n\nexport interface ApiKeyCredential {\n type: 'apiKey';\n tenantId: string;\n apiKey: string;\n basePath: string;\n}\n\nexport interface UserAuth {\n accessToken: string;\n refreshToken: string;\n expiresAt: string;\n tenant?: string; // Tenant used for login (Keycloak client ID)\n realm?: string; // Keycloak realm (default: master)\n}\n\nexport interface CurrentCheckout {\n organisation: string; // For URL: https://<organisation>.farseer.io\n tenant: string; // For X-TENANT-ID header\n}\n\nexport interface FarseerConfig {\n credentials: Record<string, ApiKeyCredential>;\n userAuth?: UserAuth;\n currentTenant?: string; // Deprecated: use currentCheckout\n currentCheckout?: CurrentCheckout; // Organisation + tenant\n tenantOrgMapping?: Record<string, string>; // Tenant -> Organisation mapping\n}\n\nconst CONFIG_DIR = path.join(os.homedir(), '.farseer');\nconst CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\n\nfunction ensureConfigDir(): void {\n if (!fs.existsSync(CONFIG_DIR)) {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n }\n}\n\nfunction getDefaultConfig(): FarseerConfig {\n return {\n credentials: {},\n userAuth: undefined\n };\n}\n\nexport function loadConfig(): FarseerConfig {\n ensureConfigDir();\n\n if (!fs.existsSync(CONFIG_FILE)) {\n return getDefaultConfig();\n }\n\n try {\n const content = fs.readFileSync(CONFIG_FILE, 'utf-8');\n return JSON.parse(content) as FarseerConfig;\n } catch {\n return getDefaultConfig();\n }\n}\n\nexport function saveConfig(config: FarseerConfig): void {\n ensureConfigDir();\n fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');\n}\n\nexport function getCredential(tenant: string): ApiKeyCredential | undefined {\n const config = loadConfig();\n return config.credentials[tenant];\n}\n\nexport function setCredential(tenant: string, credential: ApiKeyCredential): void {\n const config = loadConfig();\n config.credentials[tenant] = credential;\n saveConfig(config);\n}\n\nexport function removeCredential(tenant: string): boolean {\n const config = loadConfig();\n if (config.credentials[tenant]) {\n delete config.credentials[tenant];\n saveConfig(config);\n return true;\n }\n return false;\n}\n\nexport function listCredentials(): Record<string, ApiKeyCredential> {\n const config = loadConfig();\n return config.credentials;\n}\n\nexport function getUserAuth(): UserAuth | undefined {\n const config = loadConfig();\n return config.userAuth;\n}\n\nexport function setUserAuth(auth: UserAuth): void {\n const config = loadConfig();\n config.userAuth = auth;\n saveConfig(config);\n}\n\nexport function clearUserAuth(): void {\n const config = loadConfig();\n config.userAuth = undefined;\n saveConfig(config);\n}\n\nexport function isUserAuthValid(): boolean {\n const auth = getUserAuth();\n if (!auth) return false;\n\n const expiresAt = new Date(auth.expiresAt);\n // Add 10 second buffer to proactively refresh before actual expiry\n // Note: Keycloak tokens typically last 60 seconds, so buffer must be small\n const bufferMs = 10 * 1000;\n return expiresAt.getTime() > (Date.now() + bufferMs);\n}\n\nexport function generateBasePath(organisation: string): string {\n return `https://${organisation}.farseer.io/api/v3`;\n}\n\n// Current checkout management\nexport function getCurrentCheckout(): CurrentCheckout | undefined {\n const config = loadConfig();\n // Support both new and old format\n if (config.currentCheckout) {\n return config.currentCheckout;\n }\n // Fallback to old format (tenant only, assume same as org)\n if (config.currentTenant) {\n return { organisation: config.currentTenant, tenant: config.currentTenant };\n }\n return undefined;\n}\n\nexport function getCurrentTenant(): string | undefined {\n const checkout = getCurrentCheckout();\n return checkout?.tenant;\n}\n\nexport function getCurrentOrganisation(): string | undefined {\n const checkout = getCurrentCheckout();\n return checkout?.organisation;\n}\n\nexport function setCurrentCheckout(organisation: string, tenant: string): void {\n const config = loadConfig();\n config.currentCheckout = { organisation, tenant };\n config.currentTenant = tenant; // Keep for backward compatibility\n saveConfig(config);\n}\n\nexport function setCurrentTenant(tenant: string): void {\n // Assume organisation = tenant when only tenant is specified\n setCurrentCheckout(tenant, tenant);\n}\n\nexport function clearCurrentTenant(): void {\n const config = loadConfig();\n config.currentTenant = undefined;\n config.currentCheckout = undefined;\n saveConfig(config);\n}\n\n// Tenant-Organisation mapping\nexport function saveTenantOrgMapping(tenant: string, organisation: string): void {\n const config = loadConfig();\n if (!config.tenantOrgMapping) {\n config.tenantOrgMapping = {};\n }\n config.tenantOrgMapping[tenant] = organisation;\n saveConfig(config);\n}\n\nexport function getTenantOrg(tenant: string): string | undefined {\n const config = loadConfig();\n return config.tenantOrgMapping?.[tenant];\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FarseerApi - Direct HTTP API client using JWT Bearer token authentication
|
|
3
|
+
*
|
|
4
|
+
* This is an alternative to FarseerService that doesn't use farseer-client library.
|
|
5
|
+
* Use this when authenticating via browser login (Keycloak JWT token) instead of API key.
|
|
6
|
+
*/
|
|
7
|
+
export interface RemoteFile {
|
|
8
|
+
name: string;
|
|
9
|
+
path: string;
|
|
10
|
+
reference: string;
|
|
11
|
+
}
|
|
12
|
+
export interface AppArgument {
|
|
13
|
+
name: string;
|
|
14
|
+
type: 'variable';
|
|
15
|
+
defaultValue: string;
|
|
16
|
+
}
|
|
17
|
+
export interface AppScriptFile {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
}
|
|
21
|
+
export interface RemoteApp {
|
|
22
|
+
id: number;
|
|
23
|
+
name: string;
|
|
24
|
+
description: string;
|
|
25
|
+
expectedArguments: AppArgument[];
|
|
26
|
+
status: string;
|
|
27
|
+
lastHeartbeat: string;
|
|
28
|
+
mainScriptFileId: string | null;
|
|
29
|
+
scriptFiles: AppScriptFile[];
|
|
30
|
+
creationType: string;
|
|
31
|
+
predefinedActions: unknown[];
|
|
32
|
+
}
|
|
33
|
+
export interface AppListItem {
|
|
34
|
+
id: number;
|
|
35
|
+
name: string;
|
|
36
|
+
type: string;
|
|
37
|
+
reference: string;
|
|
38
|
+
}
|
|
39
|
+
export declare class FarseerApi {
|
|
40
|
+
private client;
|
|
41
|
+
private tenant;
|
|
42
|
+
private tenantId;
|
|
43
|
+
constructor(accessToken: string, tenant: string, tenantId?: string);
|
|
44
|
+
testConnection(): Promise<boolean>;
|
|
45
|
+
getFilesFolder(): Promise<{
|
|
46
|
+
id: number;
|
|
47
|
+
name: string;
|
|
48
|
+
type: string;
|
|
49
|
+
} | null>;
|
|
50
|
+
private listFolderItems;
|
|
51
|
+
private findFolderByName;
|
|
52
|
+
private getItemByPath;
|
|
53
|
+
listFiles(folderPath?: string[]): Promise<RemoteFile[]>;
|
|
54
|
+
getFileContent(reference: string): Promise<string>;
|
|
55
|
+
getFileContentAsBuffer(reference: string): Promise<Buffer>;
|
|
56
|
+
createFile(content: string | Buffer, fileName: string, folderPath?: string[]): Promise<void>;
|
|
57
|
+
updateFile(reference: string, content: string | Buffer, fileName: string): Promise<void>;
|
|
58
|
+
deleteFile(reference: string): Promise<void>;
|
|
59
|
+
private ensureFolderPath;
|
|
60
|
+
getFileByPath(filePath: string): Promise<RemoteFile | null>;
|
|
61
|
+
isTextFile(filename: string): boolean;
|
|
62
|
+
isBinaryFile(filename: string): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Get the Apps folder ID
|
|
65
|
+
*/
|
|
66
|
+
getAppsFolderId(): Promise<number | null>;
|
|
67
|
+
/**
|
|
68
|
+
* List all apps (Remote Jobs) on the tenant
|
|
69
|
+
*/
|
|
70
|
+
listApps(): Promise<AppListItem[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Get app details by reference ID
|
|
73
|
+
*/
|
|
74
|
+
getApp(referenceId: string): Promise<RemoteApp | null>;
|
|
75
|
+
/**
|
|
76
|
+
* Get app by name
|
|
77
|
+
*/
|
|
78
|
+
getAppByName(name: string): Promise<RemoteApp | null>;
|
|
79
|
+
/**
|
|
80
|
+
* Create a new app
|
|
81
|
+
*/
|
|
82
|
+
createApp(name: string): Promise<RemoteApp>;
|
|
83
|
+
/**
|
|
84
|
+
* Update an app
|
|
85
|
+
*/
|
|
86
|
+
updateApp(referenceId: string, update: {
|
|
87
|
+
name?: string;
|
|
88
|
+
description?: string;
|
|
89
|
+
expectedArguments?: AppArgument[];
|
|
90
|
+
mainScriptFileId?: string | null;
|
|
91
|
+
scriptFileIds?: string[];
|
|
92
|
+
}): Promise<RemoteApp>;
|
|
93
|
+
/**
|
|
94
|
+
* Delete an app
|
|
95
|
+
*/
|
|
96
|
+
deleteApp(referenceId: string): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Run an app with arguments
|
|
99
|
+
*/
|
|
100
|
+
runApp(referenceId: string, args?: Record<string, string>): Promise<{
|
|
101
|
+
executionId: string;
|
|
102
|
+
}>;
|
|
103
|
+
/**
|
|
104
|
+
* Get script files available for apps (searches all .ts/.js files in Files folder)
|
|
105
|
+
*/
|
|
106
|
+
getScriptFiles(): Promise<AppScriptFile[]>;
|
|
107
|
+
/**
|
|
108
|
+
* Export the entire model structure - all dimension tables, variables, and configurations.
|
|
109
|
+
* Essential for understanding a tenant's data model.
|
|
110
|
+
*/
|
|
111
|
+
exportModel(): Promise<ModelExport>;
|
|
112
|
+
}
|
|
113
|
+
export interface ModelExport {
|
|
114
|
+
tables: ModelExportTable[];
|
|
115
|
+
variables: ModelExportVariable[];
|
|
116
|
+
dependencies: {
|
|
117
|
+
tables: string[];
|
|
118
|
+
members: string[];
|
|
119
|
+
variables: string[];
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export interface ModelExportTable {
|
|
123
|
+
name: string;
|
|
124
|
+
columns: ModelExportColumn[];
|
|
125
|
+
defaultMember?: string | null;
|
|
126
|
+
folderPath?: string[];
|
|
127
|
+
rows: any[][];
|
|
128
|
+
autoIncrementTemplate?: string | null;
|
|
129
|
+
}
|
|
130
|
+
export interface ModelExportColumn {
|
|
131
|
+
name?: string;
|
|
132
|
+
type: string;
|
|
133
|
+
foreignKeyTableName?: string;
|
|
134
|
+
}
|
|
135
|
+
export interface ModelExportVariable {
|
|
136
|
+
name: string;
|
|
137
|
+
description?: string | null;
|
|
138
|
+
formula?: string | null;
|
|
139
|
+
tables: string[];
|
|
140
|
+
members: string[];
|
|
141
|
+
rollupType: string;
|
|
142
|
+
dataType: string;
|
|
143
|
+
numberFormat?: {
|
|
144
|
+
type: string;
|
|
145
|
+
custom?: boolean;
|
|
146
|
+
formatString?: string;
|
|
147
|
+
decimals?: number;
|
|
148
|
+
};
|
|
149
|
+
numberValidation?: {
|
|
150
|
+
min?: number | null;
|
|
151
|
+
max?: number | null;
|
|
152
|
+
};
|
|
153
|
+
topDownMapping?: string | null;
|
|
154
|
+
readonly: boolean;
|
|
155
|
+
calculateOnDemand: boolean;
|
|
156
|
+
zConstraint?: boolean;
|
|
157
|
+
active?: boolean;
|
|
158
|
+
optimizeCalculation?: boolean;
|
|
159
|
+
useHierarchy?: boolean;
|
|
160
|
+
folderPath?: string[];
|
|
161
|
+
cells?: ModelExportCell[];
|
|
162
|
+
}
|
|
163
|
+
export interface ModelExportCell {
|
|
164
|
+
value: number;
|
|
165
|
+
members: string[];
|
|
166
|
+
}
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
var farseerApi_exports = {};
|
|
29
|
+
__export(farseerApi_exports, {
|
|
30
|
+
FarseerApi: () => FarseerApi
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(farseerApi_exports);
|
|
33
|
+
var import_axios = __toESM(require("axios"));
|
|
34
|
+
var import_configService = require("./configService");
|
|
35
|
+
class FarseerApi {
|
|
36
|
+
constructor(accessToken, tenant, tenantId) {
|
|
37
|
+
this.tenant = tenant;
|
|
38
|
+
this.tenantId = tenantId || tenant;
|
|
39
|
+
const credential = (0, import_configService.getCredential)(tenant);
|
|
40
|
+
const basePath = credential?.basePath || `https://${tenant}.farseer.io/api/v3`;
|
|
41
|
+
this.client = import_axios.default.create({
|
|
42
|
+
baseURL: basePath,
|
|
43
|
+
headers: {
|
|
44
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
45
|
+
"Content-Type": "application/json",
|
|
46
|
+
"x-api-version": "3.3.0",
|
|
47
|
+
"X-TENANT-ID": this.tenantId
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Update the access token (used when token is refreshed)
|
|
53
|
+
*/
|
|
54
|
+
updateAccessToken(newToken) {
|
|
55
|
+
this.client.defaults.headers["Authorization"] = `Bearer ${newToken}`;
|
|
56
|
+
}
|
|
57
|
+
async testConnection() {
|
|
58
|
+
try {
|
|
59
|
+
await this.getItemByPath(["Files"]);
|
|
60
|
+
return true;
|
|
61
|
+
} catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async getFilesFolder() {
|
|
66
|
+
try {
|
|
67
|
+
const item = await this.getItemByPath(["Files"]);
|
|
68
|
+
return {
|
|
69
|
+
id: item.id,
|
|
70
|
+
name: item.name,
|
|
71
|
+
type: item.type
|
|
72
|
+
};
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async listFolderItems(folderId) {
|
|
78
|
+
const response = await this.client.post("/folders/items", [{ id: folderId }]);
|
|
79
|
+
return response.data[0] || { items: [] };
|
|
80
|
+
}
|
|
81
|
+
async findFolderByName(parentId, name) {
|
|
82
|
+
const content = await this.listFolderItems(parentId);
|
|
83
|
+
const folder = content.items?.find(
|
|
84
|
+
(item) => item.name.toLowerCase() === name.toLowerCase() && item.type.toLowerCase() === "folder"
|
|
85
|
+
);
|
|
86
|
+
return folder || null;
|
|
87
|
+
}
|
|
88
|
+
async getItemByPath(path) {
|
|
89
|
+
let currentId = null;
|
|
90
|
+
for (const segment of path) {
|
|
91
|
+
const folder = await this.findFolderByName(currentId, segment);
|
|
92
|
+
if (!folder) {
|
|
93
|
+
throw new Error(`Folder not found: ${segment}`);
|
|
94
|
+
}
|
|
95
|
+
currentId = folder.id;
|
|
96
|
+
}
|
|
97
|
+
if (currentId === null) {
|
|
98
|
+
throw new Error("Path is empty");
|
|
99
|
+
}
|
|
100
|
+
return { id: currentId, name: path[path.length - 1], type: "folder" };
|
|
101
|
+
}
|
|
102
|
+
async listFiles(folderPath = ["Files"]) {
|
|
103
|
+
const files = [];
|
|
104
|
+
try {
|
|
105
|
+
const folder = await this.getItemByPath(folderPath);
|
|
106
|
+
const folderContent = await this.listFolderItems(folder.id);
|
|
107
|
+
for (const item of folderContent.items || []) {
|
|
108
|
+
const itemPath = [...folderPath.slice(1), item.name].join("/");
|
|
109
|
+
const itemType = item.type.toLowerCase();
|
|
110
|
+
if (itemType === "folder") {
|
|
111
|
+
const subFiles = await this.listFiles([...folderPath, item.name]);
|
|
112
|
+
files.push(...subFiles);
|
|
113
|
+
} else if ((itemType === "farseer-file" || itemType === "farseer_file") && item.reference) {
|
|
114
|
+
files.push({
|
|
115
|
+
name: item.name,
|
|
116
|
+
path: itemPath,
|
|
117
|
+
reference: item.reference
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (import_axios.default.isAxiosError(error)) {
|
|
123
|
+
const status = error.response?.status;
|
|
124
|
+
const data = error.response?.data;
|
|
125
|
+
const message = typeof data === "string" ? data : data?.message || JSON.stringify(data);
|
|
126
|
+
console.error(`API Error: ${status} "${message}" (path: ${folderPath.join("/")})`);
|
|
127
|
+
if (data?.code === "Forbidden") {
|
|
128
|
+
const errorMessage = data?.message || "Access denied";
|
|
129
|
+
throw new Error(`Access denied: ${errorMessage}
|
|
130
|
+
You may need to use --tenant-id to specify a different tenant ID.`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
return files;
|
|
136
|
+
}
|
|
137
|
+
async getFileContent(reference) {
|
|
138
|
+
const response = await this.client.get(`/farseerFiles/${reference}`, {
|
|
139
|
+
responseType: "text"
|
|
140
|
+
});
|
|
141
|
+
return response.data;
|
|
142
|
+
}
|
|
143
|
+
async getFileContentAsBuffer(reference) {
|
|
144
|
+
const response = await this.client.get(`/farseerFiles/${reference}`, {
|
|
145
|
+
responseType: "arraybuffer"
|
|
146
|
+
});
|
|
147
|
+
return Buffer.from(response.data);
|
|
148
|
+
}
|
|
149
|
+
async createFile(content, fileName, folderPath = ["Files"]) {
|
|
150
|
+
await this.ensureFolderPath(folderPath);
|
|
151
|
+
const folder = await this.getItemByPath(folderPath);
|
|
152
|
+
const formData = new FormData();
|
|
153
|
+
const blob = content instanceof Buffer ? new Blob([content]) : new Blob([content]);
|
|
154
|
+
formData.append("file", blob, fileName);
|
|
155
|
+
formData.append("category", "GENERAL");
|
|
156
|
+
formData.append("folderId", folder.id.toString());
|
|
157
|
+
await this.client.post("/farseerFiles", formData, {
|
|
158
|
+
headers: {
|
|
159
|
+
"Content-Type": "multipart/form-data"
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
async updateFile(reference, content, fileName) {
|
|
164
|
+
const formData = new FormData();
|
|
165
|
+
const blob = content instanceof Buffer ? new Blob([content]) : new Blob([content]);
|
|
166
|
+
formData.append("file", blob, fileName);
|
|
167
|
+
await this.client.put(`/farseerFiles/${reference}`, formData, {
|
|
168
|
+
headers: {
|
|
169
|
+
"Content-Type": "multipart/form-data"
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
async deleteFile(reference) {
|
|
174
|
+
await this.client.delete(`/farseerFiles/${reference}`);
|
|
175
|
+
}
|
|
176
|
+
async ensureFolderPath(folderPath) {
|
|
177
|
+
let currentPath = [];
|
|
178
|
+
for (const segment of folderPath) {
|
|
179
|
+
currentPath.push(segment);
|
|
180
|
+
try {
|
|
181
|
+
await this.getItemByPath(currentPath);
|
|
182
|
+
} catch {
|
|
183
|
+
if (currentPath.length > 1) {
|
|
184
|
+
const parentPath = currentPath.slice(0, -1);
|
|
185
|
+
const parent = await this.getItemByPath(parentPath);
|
|
186
|
+
await this.client.post("/folders", {
|
|
187
|
+
parentId: parent.id,
|
|
188
|
+
name: segment,
|
|
189
|
+
allowedTypes: []
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async getFileByPath(filePath) {
|
|
196
|
+
const pathParts = ["Files", ...filePath.split("/")];
|
|
197
|
+
const fileName = pathParts.pop();
|
|
198
|
+
const folderPath = pathParts;
|
|
199
|
+
try {
|
|
200
|
+
const folder = await this.getItemByPath(folderPath);
|
|
201
|
+
const folderContent = await this.listFolderItems(folder.id);
|
|
202
|
+
for (const item of folderContent.items || []) {
|
|
203
|
+
const itemType = item.type.toLowerCase();
|
|
204
|
+
if (item.name === fileName && (itemType === "farseer-file" || itemType === "farseer_file") && item.reference) {
|
|
205
|
+
return {
|
|
206
|
+
name: item.name,
|
|
207
|
+
path: filePath,
|
|
208
|
+
reference: item.reference
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
async getFileMetadata(reference) {
|
|
218
|
+
try {
|
|
219
|
+
const response = await this.axios.get(`/farseerFiles/${reference}/metadata`);
|
|
220
|
+
const data = response.data;
|
|
221
|
+
return {
|
|
222
|
+
uploadTime: data.uploadTime,
|
|
223
|
+
uploader: data.uploader ? {
|
|
224
|
+
id: data.uploader.id,
|
|
225
|
+
email: data.uploader.email || "",
|
|
226
|
+
firstName: data.uploader.firstName || "",
|
|
227
|
+
lastName: data.uploader.lastName || ""
|
|
228
|
+
} : void 0,
|
|
229
|
+
size: data.size
|
|
230
|
+
};
|
|
231
|
+
} catch (error) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
isTextFile(filename) {
|
|
236
|
+
const ext = filename.toLowerCase().split(".").pop() || "";
|
|
237
|
+
return ["ts", "js", "mjs", "cjs", "json", "txt", "md", "csv", "xml", "html", "css", "yaml", "yml"].includes(ext);
|
|
238
|
+
}
|
|
239
|
+
isBinaryFile(filename) {
|
|
240
|
+
return !this.isTextFile(filename);
|
|
241
|
+
}
|
|
242
|
+
// ==================== Apps (Remote Jobs) API ====================
|
|
243
|
+
/**
|
|
244
|
+
* Get the Apps folder ID
|
|
245
|
+
*/
|
|
246
|
+
async getAppsFolderId() {
|
|
247
|
+
const content = await this.listFolderItems(null);
|
|
248
|
+
const appsFolder = content.items?.find(
|
|
249
|
+
(item) => item.name.toLowerCase() === "apps" && item.type.toLowerCase() === "folder"
|
|
250
|
+
);
|
|
251
|
+
return appsFolder?.id || null;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* List all apps (Remote Jobs) on the tenant, including those in subfolders
|
|
255
|
+
*/
|
|
256
|
+
async listApps() {
|
|
257
|
+
const appsFolderId = await this.getAppsFolderId();
|
|
258
|
+
if (!appsFolderId) {
|
|
259
|
+
return [];
|
|
260
|
+
}
|
|
261
|
+
return this.listAppsRecursive(appsFolderId);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Recursively list all apps in a folder and its subfolders
|
|
265
|
+
*/
|
|
266
|
+
async listAppsRecursive(folderId) {
|
|
267
|
+
const apps = [];
|
|
268
|
+
const content = await this.listFolderItems(folderId);
|
|
269
|
+
for (const item of content.items || []) {
|
|
270
|
+
const itemType = item.type.toLowerCase();
|
|
271
|
+
if (itemType === "remote_job") {
|
|
272
|
+
apps.push({
|
|
273
|
+
id: item.id,
|
|
274
|
+
name: item.name,
|
|
275
|
+
type: item.type,
|
|
276
|
+
reference: item.reference || ""
|
|
277
|
+
});
|
|
278
|
+
} else if (itemType === "folder") {
|
|
279
|
+
const subApps = await this.listAppsRecursive(item.id);
|
|
280
|
+
apps.push(...subApps);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return apps;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Get app details by reference ID
|
|
287
|
+
*/
|
|
288
|
+
async getApp(referenceId) {
|
|
289
|
+
try {
|
|
290
|
+
const response = await this.client.get(`/remoteJobs/${referenceId}`);
|
|
291
|
+
return response.data;
|
|
292
|
+
} catch {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Get app by name
|
|
298
|
+
*/
|
|
299
|
+
async getAppByName(name) {
|
|
300
|
+
const apps = await this.listApps();
|
|
301
|
+
const app = apps.find((a) => a.name.toLowerCase() === name.toLowerCase());
|
|
302
|
+
if (!app) {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
return this.getApp(app.reference);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Create a new app
|
|
309
|
+
*/
|
|
310
|
+
async createApp(name) {
|
|
311
|
+
const appsFolderId = await this.getAppsFolderId();
|
|
312
|
+
if (!appsFolderId) {
|
|
313
|
+
throw new Error("Apps folder not found");
|
|
314
|
+
}
|
|
315
|
+
const response = await this.client.post("/remoteJobs", {
|
|
316
|
+
folderId: appsFolderId,
|
|
317
|
+
name
|
|
318
|
+
});
|
|
319
|
+
return response.data;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Update an app
|
|
323
|
+
*/
|
|
324
|
+
async updateApp(referenceId, update) {
|
|
325
|
+
await this.client.put(`/remoteJobs/${referenceId}`, {
|
|
326
|
+
...update,
|
|
327
|
+
schedule: null,
|
|
328
|
+
runnerName: null,
|
|
329
|
+
predefinedActions: []
|
|
330
|
+
});
|
|
331
|
+
const app = await this.getApp(referenceId);
|
|
332
|
+
if (!app) {
|
|
333
|
+
throw new Error("Failed to fetch updated app");
|
|
334
|
+
}
|
|
335
|
+
return app;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Delete an app
|
|
339
|
+
*/
|
|
340
|
+
async deleteApp(referenceId) {
|
|
341
|
+
await this.client.delete(`/remoteJobs/${referenceId}`);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Run an app with arguments
|
|
345
|
+
*/
|
|
346
|
+
async runApp(referenceId, args = {}) {
|
|
347
|
+
const response = await this.client.post(`/remoteJobs/${referenceId}/run`, {
|
|
348
|
+
arguments: args
|
|
349
|
+
});
|
|
350
|
+
return response.data;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Get script files available for apps (searches all .ts/.js files in Files folder)
|
|
354
|
+
* Returns full path relative to Files/ folder (e.g., "Scripts/test.ts", "Scripts/subfolder/helper.ts")
|
|
355
|
+
*/
|
|
356
|
+
async getScriptFiles() {
|
|
357
|
+
const allFiles = await this.listFiles(["Files"]);
|
|
358
|
+
return allFiles.filter((f) => f.name.endsWith(".ts") || f.name.endsWith(".js")).map((f) => ({
|
|
359
|
+
id: f.reference,
|
|
360
|
+
name: f.path
|
|
361
|
+
// Use full path to support scripts in subfolders
|
|
362
|
+
}));
|
|
363
|
+
}
|
|
364
|
+
// ==================== Model Export API ====================
|
|
365
|
+
/**
|
|
366
|
+
* Export the entire model structure - all dimension tables, variables, and configurations.
|
|
367
|
+
* Essential for understanding a tenant's data model.
|
|
368
|
+
*/
|
|
369
|
+
async exportModel() {
|
|
370
|
+
const response = await this.client.post("/model/export");
|
|
371
|
+
return response.data;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
375
|
+
0 && (module.exports = {
|
|
376
|
+
FarseerApi
|
|
377
|
+
});
|
|
378
|
+
//# sourceMappingURL=farseerApi.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/services/farseerApi.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * FarseerApi - Direct HTTP API client using JWT Bearer token authentication\n *\n * This is an alternative to FarseerService that doesn't use farseer-client library.\n * Use this when authenticating via browser login (Keycloak JWT token) instead of API key.\n */\n\nimport axios, { AxiosInstance } from 'axios';\nimport { getCredential } from './configService';\n\nexport interface RemoteFile {\n name: string;\n path: string;\n reference: string;\n}\n\ninterface FolderItem {\n id: number;\n name: string;\n type: 'folder' | 'farseer-file';\n reference?: string;\n}\n\ninterface FolderContent {\n items: FolderItem[];\n}\n\n// App (Remote Job) types\nexport interface AppArgument {\n name: string;\n type: 'variable';\n defaultValue: string;\n}\n\nexport interface AppScriptFile {\n id: string;\n name: string;\n}\n\nexport interface RemoteApp {\n id: number;\n name: string;\n description: string;\n expectedArguments: AppArgument[];\n status: string;\n lastHeartbeat: string;\n mainScriptFileId: string | null;\n scriptFiles: AppScriptFile[];\n creationType: string;\n predefinedActions: unknown[];\n}\n\nexport interface AppListItem {\n id: number;\n name: string;\n type: string;\n reference: string;\n}\n\nexport class FarseerApi {\n private client: AxiosInstance;\n private tenant: string;\n private tenantId: string;\n\n constructor(accessToken: string, tenant: string, tenantId?: string) {\n this.tenant = tenant;\n this.tenantId = tenantId || tenant;\n\n // Try to get basePath from existing config, or generate from tenant name\n const credential = getCredential(tenant);\n const basePath = credential?.basePath || `https://${tenant}.farseer.io/api/v3`;\n\n this.client = axios.create({\n baseURL: basePath,\n headers: {\n 'Authorization': `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n 'x-api-version': '3.3.0',\n 'X-TENANT-ID': this.tenantId,\n },\n });\n }\n\n /**\n * Update the access token (used when token is refreshed)\n */\n updateAccessToken(newToken: string): void {\n this.client.defaults.headers['Authorization'] = `Bearer ${newToken}`;\n }\n\n async testConnection(): Promise<boolean> {\n try {\n await this.getItemByPath(['Files']);\n return true;\n } catch {\n return false;\n }\n }\n\n async getFilesFolder(): Promise<{ id: number; name: string; type: string } | null> {\n try {\n const item = await this.getItemByPath(['Files']);\n return {\n id: item.id,\n name: item.name,\n type: item.type,\n };\n } catch {\n return null;\n }\n }\n\n private async listFolderItems(folderId: number | null): Promise<FolderContent> {\n // Body is array: [{ id: null }] for root, [{ id: folderId }] for specific folder\n const response = await this.client.post('/folders/items', [{ id: folderId }]);\n return response.data[0] || { items: [] };\n }\n\n private async findFolderByName(parentId: number | null, name: string): Promise<FolderItem | null> {\n const content = await this.listFolderItems(parentId);\n const folder = content.items?.find(\n (item) => item.name.toLowerCase() === name.toLowerCase() && item.type.toLowerCase() === 'folder'\n );\n return folder || null;\n }\n\n private async getItemByPath(path: string[]): Promise<FolderItem> {\n let currentId: number | null = null;\n\n for (const segment of path) {\n const folder = await this.findFolderByName(currentId, segment);\n if (!folder) {\n throw new Error(`Folder not found: ${segment}`);\n }\n currentId = folder.id;\n }\n\n if (currentId === null) {\n throw new Error('Path is empty');\n }\n\n // Return a minimal FolderItem with the found id\n return { id: currentId, name: path[path.length - 1], type: 'folder' };\n }\n\n async listFiles(folderPath: string[] = ['Files']): Promise<RemoteFile[]> {\n const files: RemoteFile[] = [];\n\n try {\n const folder = await this.getItemByPath(folderPath);\n const folderContent = await this.listFolderItems(folder.id);\n\n for (const item of folderContent.items || []) {\n const itemPath = [...folderPath.slice(1), item.name].join('/');\n const itemType = item.type.toLowerCase();\n\n if (itemType === 'folder') {\n const subFiles = await this.listFiles([...folderPath, item.name]);\n files.push(...subFiles);\n } else if ((itemType === 'farseer-file' || itemType === 'farseer_file') && item.reference) {\n files.push({\n name: item.name,\n path: itemPath,\n reference: item.reference,\n });\n }\n }\n } catch (error) {\n if (axios.isAxiosError(error)) {\n const status = error.response?.status;\n const data = error.response?.data;\n const message = typeof data === 'string' ? data : (data?.message || JSON.stringify(data));\n console.error(`API Error: ${status} \"${message}\" (path: ${folderPath.join('/')})`);\n if (data?.code === 'Forbidden') {\n const errorMessage = data?.message || 'Access denied';\n throw new Error(`Access denied: ${errorMessage}\\nYou may need to use --tenant-id to specify a different tenant ID.`);\n }\n }\n return [];\n }\n\n return files;\n }\n\n async getFileContent(reference: string): Promise<string> {\n const response = await this.client.get(`/farseerFiles/${reference}`, {\n responseType: 'text',\n });\n return response.data;\n }\n\n async getFileContentAsBuffer(reference: string): Promise<Buffer> {\n const response = await this.client.get(`/farseerFiles/${reference}`, {\n responseType: 'arraybuffer',\n });\n return Buffer.from(response.data);\n }\n\n async createFile(content: string | Buffer, fileName: string, folderPath: string[] = ['Files']): Promise<void> {\n await this.ensureFolderPath(folderPath);\n\n const folder = await this.getItemByPath(folderPath);\n\n const formData = new FormData();\n const blob = content instanceof Buffer ? new Blob([content]) : new Blob([content]);\n formData.append('file', blob, fileName);\n formData.append('category', 'GENERAL');\n formData.append('folderId', folder.id.toString());\n\n await this.client.post('/farseerFiles', formData, {\n headers: {\n 'Content-Type': 'multipart/form-data',\n },\n });\n }\n\n async updateFile(reference: string, content: string | Buffer, fileName: string): Promise<void> {\n const formData = new FormData();\n const blob = content instanceof Buffer ? new Blob([content]) : new Blob([content]);\n formData.append('file', blob, fileName);\n\n await this.client.put(`/farseerFiles/${reference}`, formData, {\n headers: {\n 'Content-Type': 'multipart/form-data',\n },\n });\n }\n\n async deleteFile(reference: string): Promise<void> {\n await this.client.delete(`/farseerFiles/${reference}`);\n }\n\n private async ensureFolderPath(folderPath: string[]): Promise<void> {\n let currentPath: string[] = [];\n\n for (const segment of folderPath) {\n currentPath.push(segment);\n try {\n await this.getItemByPath(currentPath);\n } catch {\n if (currentPath.length > 1) {\n const parentPath = currentPath.slice(0, -1);\n const parent = await this.getItemByPath(parentPath);\n await this.client.post('/folders', {\n parentId: parent.id,\n name: segment,\n allowedTypes: [],\n });\n }\n }\n }\n }\n\n async getFileByPath(filePath: string): Promise<RemoteFile | null> {\n const pathParts = ['Files', ...filePath.split('/')];\n const fileName = pathParts.pop()!;\n const folderPath = pathParts;\n\n try {\n const folder = await this.getItemByPath(folderPath);\n const folderContent = await this.listFolderItems(folder.id);\n\n for (const item of folderContent.items || []) {\n const itemType = item.type.toLowerCase();\n if (item.name === fileName && (itemType === 'farseer-file' || itemType === 'farseer_file') && item.reference) {\n return {\n name: item.name,\n path: filePath,\n reference: item.reference,\n };\n }\n }\n } catch {\n return null;\n }\n\n return null;\n }\n\n async getFileMetadata(reference: string): Promise<import('./farseerFactory').FileMetadata | null> {\n try {\n const response = await this.axios.get(`/farseerFiles/${reference}/metadata`);\n const data = response.data;\n\n return {\n uploadTime: data.uploadTime,\n uploader: data.uploader ? {\n id: data.uploader.id,\n email: data.uploader.email || '',\n firstName: data.uploader.firstName || '',\n lastName: data.uploader.lastName || '',\n } : undefined,\n size: data.size,\n };\n } catch (error) {\n // Metadata fetch failed - non-critical, continue without it\n return null;\n }\n }\n\n isTextFile(filename: string): boolean {\n const ext = filename.toLowerCase().split('.').pop() || '';\n return ['ts', 'js', 'mjs', 'cjs', 'json', 'txt', 'md', 'csv', 'xml', 'html', 'css', 'yaml', 'yml'].includes(ext);\n }\n\n isBinaryFile(filename: string): boolean {\n return !this.isTextFile(filename);\n }\n\n // ==================== Apps (Remote Jobs) API ====================\n\n /**\n * Get the Apps folder ID\n */\n async getAppsFolderId(): Promise<number | null> {\n const content = await this.listFolderItems(null);\n const appsFolder = content.items?.find(\n (item) => item.name.toLowerCase() === 'apps' && item.type.toLowerCase() === 'folder'\n );\n return appsFolder?.id || null;\n }\n\n /**\n * List all apps (Remote Jobs) on the tenant, including those in subfolders\n */\n async listApps(): Promise<AppListItem[]> {\n const appsFolderId = await this.getAppsFolderId();\n if (!appsFolderId) {\n return [];\n }\n\n return this.listAppsRecursive(appsFolderId);\n }\n\n /**\n * Recursively list all apps in a folder and its subfolders\n */\n private async listAppsRecursive(folderId: number): Promise<AppListItem[]> {\n const apps: AppListItem[] = [];\n const content = await this.listFolderItems(folderId);\n\n for (const item of content.items || []) {\n const itemType = item.type.toLowerCase();\n\n if (itemType === 'remote_job') {\n // Found an app\n apps.push({\n id: item.id,\n name: item.name,\n type: item.type,\n reference: item.reference || '',\n });\n } else if (itemType === 'folder') {\n // Recurse into subfolder\n const subApps = await this.listAppsRecursive(item.id);\n apps.push(...subApps);\n }\n }\n\n return apps;\n }\n\n /**\n * Get app details by reference ID\n */\n async getApp(referenceId: string): Promise<RemoteApp | null> {\n try {\n const response = await this.client.get(`/remoteJobs/${referenceId}`);\n return response.data;\n } catch {\n return null;\n }\n }\n\n /**\n * Get app by name\n */\n async getAppByName(name: string): Promise<RemoteApp | null> {\n const apps = await this.listApps();\n const app = apps.find((a) => a.name.toLowerCase() === name.toLowerCase());\n if (!app) {\n return null;\n }\n return this.getApp(app.reference);\n }\n\n /**\n * Create a new app\n */\n async createApp(name: string): Promise<RemoteApp> {\n const appsFolderId = await this.getAppsFolderId();\n if (!appsFolderId) {\n throw new Error('Apps folder not found');\n }\n\n const response = await this.client.post('/remoteJobs', {\n folderId: appsFolderId,\n name,\n });\n return response.data;\n }\n\n /**\n * Update an app\n */\n async updateApp(\n referenceId: string,\n update: {\n name?: string;\n description?: string;\n expectedArguments?: AppArgument[];\n mainScriptFileId?: string | null;\n scriptFileIds?: string[];\n }\n ): Promise<RemoteApp> {\n await this.client.put(`/remoteJobs/${referenceId}`, {\n ...update,\n schedule: null,\n runnerName: null,\n predefinedActions: [],\n });\n // API returns 204 No Content, so fetch the updated app\n const app = await this.getApp(referenceId);\n if (!app) {\n throw new Error('Failed to fetch updated app');\n }\n return app;\n }\n\n /**\n * Delete an app\n */\n async deleteApp(referenceId: string): Promise<void> {\n await this.client.delete(`/remoteJobs/${referenceId}`);\n }\n\n /**\n * Run an app with arguments\n */\n async runApp(\n referenceId: string,\n args: Record<string, string> = {}\n ): Promise<{ executionId: string }> {\n const response = await this.client.post(`/remoteJobs/${referenceId}/run`, {\n arguments: args,\n });\n return response.data;\n }\n\n /**\n * Get script files available for apps (searches all .ts/.js files in Files folder)\n * Returns full path relative to Files/ folder (e.g., \"Scripts/test.ts\", \"Scripts/subfolder/helper.ts\")\n */\n async getScriptFiles(): Promise<AppScriptFile[]> {\n // listFiles already searches recursively, so just filter for .ts/.js files\n const allFiles = await this.listFiles(['Files']);\n return allFiles\n .filter(f => f.name.endsWith('.ts') || f.name.endsWith('.js'))\n .map(f => ({\n id: f.reference,\n name: f.path, // Use full path to support scripts in subfolders\n }));\n }\n\n // ==================== Model Export API ====================\n\n /**\n * Export the entire model structure - all dimension tables, variables, and configurations.\n * Essential for understanding a tenant's data model.\n */\n async exportModel(): Promise<ModelExport> {\n const response = await this.client.post('/model/export');\n return response.data;\n }\n}\n\n// Model Export types\nexport interface ModelExport {\n tables: ModelExportTable[];\n variables: ModelExportVariable[];\n dependencies: {\n tables: string[];\n members: string[];\n variables: string[];\n };\n}\n\nexport interface ModelExportTable {\n name: string;\n columns: ModelExportColumn[];\n defaultMember?: string | null;\n folderPath?: string[];\n rows: any[][];\n autoIncrementTemplate?: string | null;\n}\n\nexport interface ModelExportColumn {\n name?: string;\n type: string; // PRIMARY_KEY, FORMULA, ORDER, ACTIVE, DESCRIPTION, FOREIGN_KEY, NUMBER, TEXT, DATE, FILE\n foreignKeyTableName?: string;\n}\n\nexport interface ModelExportVariable {\n name: string;\n description?: string | null;\n formula?: string | null;\n tables: string[];\n members: string[];\n rollupType: string; // SUM, AVERAGE, CLOSING STATE, BEGINNING STATE, FORMULA, MAX, MIN\n dataType: string; // DECIMAL, INTEGER\n numberFormat?: {\n type: string; // NUMBER, CURRENCY, PERCENTAGE, DATE, CUSTOM\n custom?: boolean;\n formatString?: string;\n decimals?: number;\n };\n numberValidation?: {\n min?: number | null;\n max?: number | null;\n };\n topDownMapping?: string | null;\n readonly: boolean;\n calculateOnDemand: boolean;\n zConstraint?: boolean;\n active?: boolean;\n optimizeCalculation?: boolean;\n useHierarchy?: boolean;\n folderPath?: string[];\n cells?: ModelExportCell[];\n}\n\nexport interface ModelExportCell {\n value: number;\n members: string[];\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["axios"]
|
|
7
|
+
}
|