dalux-build-api 1.0.1 → 1.1.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.
@@ -1,64 +1,64 @@
1
- 'use strict';
2
-
3
- /**
4
- * API methods for chunked file uploads.
5
- */
6
- class FileUploadApi {
7
- /**
8
- * @param {import('../apiClient')} apiClient
9
- */
10
- constructor(apiClient) {
11
- this._client = apiClient;
12
- }
13
-
14
- /**
15
- * Create a new upload slot and return a GUID pointing to that slot.
16
- * POST /1.0/projects/{projectId}/file_areas/{fileAreaId}/upload
17
- * @param {string} projectId
18
- * @param {string} fileAreaId
19
- * @param {object} body
20
- * @returns {Promise<object>}
21
- */
22
- createUpload(projectId, fileAreaId, body) {
23
- return this._client.post(
24
- `/1.0/projects/${projectId}/file_areas/${fileAreaId}/upload`,
25
- body,
26
- );
27
- }
28
-
29
- /**
30
- * Upload a part of a file.
31
- * POST /1.0/projects/{projectId}/file_areas/{fileAreaId}/upload/{uploadGuid}
32
- * @param {string} projectId
33
- * @param {string} fileAreaId
34
- * @param {string} uploadGuid
35
- * @param {Buffer|Uint8Array} chunk - Binary file chunk
36
- * @returns {Promise<object>}
37
- */
38
- uploadFilePart(projectId, fileAreaId, uploadGuid, chunk) {
39
- return this._client.post(
40
- `/1.0/projects/${projectId}/file_areas/${fileAreaId}/upload/${uploadGuid}`,
41
- chunk,
42
- {},
43
- { headers: { 'Content-Type': 'application/octet-stream' } },
44
- );
45
- }
46
-
47
- /**
48
- * Finish uploading a file (finalize the upload).
49
- * POST /2.0/projects/{projectId}/file_areas/{fileAreaId}/upload/{uploadGuid}/finalize
50
- * @param {string} projectId
51
- * @param {string} fileAreaId
52
- * @param {string} uploadGuid
53
- * @param {object} body
54
- * @returns {Promise<object>}
55
- */
56
- finishUpload(projectId, fileAreaId, uploadGuid, body) {
57
- return this._client.post(
58
- `/2.0/projects/${projectId}/file_areas/${fileAreaId}/upload/${uploadGuid}/finalize`,
59
- body,
60
- );
61
- }
62
- }
63
-
64
- module.exports = FileUploadApi;
1
+ 'use strict';
2
+
3
+ /**
4
+ * API methods for chunked file uploads.
5
+ */
6
+ class FileUploadApi {
7
+ /**
8
+ * @param {import('../apiClient')} apiClient
9
+ */
10
+ constructor(apiClient) {
11
+ this._client = apiClient;
12
+ }
13
+
14
+ /**
15
+ * Create a new upload slot and return a GUID pointing to that slot.
16
+ * POST /1.0/projects/{projectId}/file_areas/{fileAreaId}/upload
17
+ * @param {string} projectId
18
+ * @param {string} fileAreaId
19
+ * @param {object} body
20
+ * @returns {Promise<object>}
21
+ */
22
+ createUpload(projectId, fileAreaId, body) {
23
+ return this._client.post(
24
+ `/1.0/projects/${projectId}/file_areas/${fileAreaId}/upload`,
25
+ body,
26
+ );
27
+ }
28
+
29
+ /**
30
+ * Upload a part of a file.
31
+ * POST /1.0/projects/{projectId}/file_areas/{fileAreaId}/upload/{uploadGuid}
32
+ * @param {string} projectId
33
+ * @param {string} fileAreaId
34
+ * @param {string} uploadGuid
35
+ * @param {Buffer|Uint8Array} chunk - Binary file chunk
36
+ * @returns {Promise<object>}
37
+ */
38
+ uploadFilePart(projectId, fileAreaId, uploadGuid, chunk) {
39
+ return this._client.post(
40
+ `/1.0/projects/${projectId}/file_areas/${fileAreaId}/upload/${uploadGuid}`,
41
+ chunk,
42
+ {},
43
+ { headers: { 'Content-Type': 'application/octet-stream' } },
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Finish uploading a file (finalize the upload).
49
+ * POST /2.0/projects/{projectId}/file_areas/{fileAreaId}/upload/{uploadGuid}/finalize
50
+ * @param {string} projectId
51
+ * @param {string} fileAreaId
52
+ * @param {string} uploadGuid
53
+ * @param {object} body
54
+ * @returns {Promise<object>}
55
+ */
56
+ finishUpload(projectId, fileAreaId, uploadGuid, body) {
57
+ return this._client.post(
58
+ `/2.0/projects/${projectId}/file_areas/${fileAreaId}/upload/${uploadGuid}/finalize`,
59
+ body,
60
+ );
61
+ }
62
+ }
63
+
64
+ module.exports = FileUploadApi;
@@ -1,72 +1,297 @@
1
- 'use strict';
2
-
3
- /**
4
- * API methods for files within a file area.
5
- */
6
- class FilesApi {
7
- /**
8
- * @param {import('../apiClient')} apiClient
9
- */
10
- constructor(apiClient) {
11
- this._client = apiClient;
12
- }
13
-
14
- /**
15
- * Browse all files on the given project and file area.
16
- * GET /6.0/projects/{projectId}/file_areas/{fileAreaId}/files
17
- * @param {string} projectId
18
- * @param {string} fileAreaId
19
- * @param {object} [params] - Optional filters (e.g. folderId, updatedAfter)
20
- * @returns {Promise<object>}
21
- */
22
- listFiles(projectId, fileAreaId, params = {}) {
23
- return this._client.get(
24
- `/6.0/projects/${projectId}/file_areas/${fileAreaId}/files`,
25
- params,
26
- );
27
- }
28
-
29
- /**
30
- * Retrieve a specific file.
31
- * GET /5.0/projects/{projectId}/file_areas/{fileAreaId}/files/{fileId}
32
- * @param {string} projectId
33
- * @param {string} fileAreaId
34
- * @param {string} fileId
35
- * @returns {Promise<object>}
36
- */
37
- getFile(projectId, fileAreaId, fileId) {
38
- return this._client.get(
39
- `/5.0/projects/${projectId}/file_areas/${fileAreaId}/files/${fileId}`,
40
- );
41
- }
42
-
43
- /**
44
- * Retrieve properties mapping for a specific file.
45
- * GET /1.0/projects/{projectId}/file_areas/{fileAreaId}/files/{fileId}/properties/1.0/mappings
46
- * @param {string} projectId
47
- * @param {string} fileAreaId
48
- * @param {string} fileId
49
- * @returns {Promise<object>}
50
- */
51
- getFilePropertiesMapping(projectId, fileAreaId, fileId) {
52
- return this._client.get(
53
- `/1.0/projects/${projectId}/file_areas/${fileAreaId}/files/${fileId}/properties/1.0/mappings`,
54
- );
55
- }
56
-
57
- /**
58
- * Retrieve valid property values for a specific file property mapping.
59
- * GET /1.0/projects/{projectId}/file_areas/{fileAreaId}/files/properties/1.0/mappings/{filePropertyId}/values
60
- * @param {string} projectId
61
- * @param {string} fileAreaId
62
- * @param {string} filePropertyId
63
- * @returns {Promise<object>}
64
- */
65
- getFilePropertyMappingValues(projectId, fileAreaId, filePropertyId) {
66
- return this._client.get(
67
- `/1.0/projects/${projectId}/file_areas/${fileAreaId}/files/properties/1.0/mappings/${filePropertyId}/values`,
68
- );
69
- }
70
- }
71
-
72
- module.exports = FilesApi;
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const axios = require('axios');
6
+
7
+ /**
8
+ * API methods for files within a file area.
9
+ */
10
+ class FilesApi {
11
+ /**
12
+ * @param {import('../apiClient')} apiClient
13
+ */
14
+ constructor(apiClient) {
15
+ this._client = apiClient;
16
+ }
17
+
18
+ /**
19
+ * Browse all files on the given project and file area.
20
+ * GET /6.1/projects/{projectId}/file_areas/{fileAreaId}/files
21
+ * @param {string} projectId
22
+ * @param {string} fileAreaId
23
+ * @param {object} [params] - Optional params (e.g. folderId, updatedAfter, includeProperties). The files endpoint does not support OData $filter.
24
+ * @returns {Promise<object>}
25
+ */
26
+ listFiles(projectId, fileAreaId, params = {}) {
27
+ return this._client.get(
28
+ `/6.1/projects/${projectId}/file_areas/${fileAreaId}/files`,
29
+ params,
30
+ );
31
+ }
32
+
33
+ /**
34
+ * Retrieve all files by following bookmark pagination (metadata.totalRemainingItems).
35
+ * @param {string} projectId
36
+ * @param {string} fileAreaId
37
+ * @param {object} [params]
38
+ * @param {boolean} [verbose=false]
39
+ * @returns {Promise<object[]>}
40
+ */
41
+ async getAllFiles(projectId, fileAreaId, params = {}, verbose = false) {
42
+ const allItems = [];
43
+ let currentParams = { ...params };
44
+ let hasNextPage = true;
45
+ const urlPath = `/6.1/projects/${projectId}/file_areas/${fileAreaId}/files`;
46
+
47
+ while (hasNextPage) {
48
+ const response = await this._client.get(urlPath, currentParams);
49
+ const items = response && response.items;
50
+ if (items && items.length) {
51
+ allItems.push(...items);
52
+ }
53
+ const remaining = ((response && response.metadata) || {}).totalRemainingItems ?? 0;
54
+ if (verbose) {
55
+ console.log(`Retrieved ${allItems.length} files so far, ${remaining} remaining...`);
56
+ }
57
+ if (!items || !items.length || remaining === 0) {
58
+ hasNextPage = false;
59
+ } else {
60
+ const nextLink = (response.links || []).find((l) => l.rel === 'nextPage');
61
+ if (nextLink) {
62
+ const bookmark = new URL(nextLink.href).searchParams.get('bookmark');
63
+ currentParams = { ...params, bookmark };
64
+ } else {
65
+ hasNextPage = false;
66
+ }
67
+ }
68
+ }
69
+ if (verbose) {
70
+ console.log(`Done. Total files retrieved: ${allItems.length}`);
71
+ }
72
+ return allItems;
73
+ }
74
+
75
+ /**
76
+ * All files in a folder (filters getAllFiles by data.folderId).
77
+ * @param {string} projectId
78
+ * @param {string} fileAreaId
79
+ * @param {string} folderId
80
+ * @param {object} [params]
81
+ * @param {boolean} [verbose=false]
82
+ * @returns {Promise<object[]>}
83
+ */
84
+ async getAllFilesInFolder(projectId, fileAreaId, folderId, params = {}, verbose = false) {
85
+ const allFiles = await this.getAllFiles(projectId, fileAreaId, params, verbose);
86
+ const filtered = allFiles.filter((f) => ((f.data) || {}).folderId === folderId);
87
+ if (verbose) {
88
+ console.log(`Files matching folder '${folderId}': ${filtered.length}`);
89
+ }
90
+ return filtered;
91
+ }
92
+
93
+ /**
94
+ * Download a file from a direct download URL using X-API-KEY (same as Python client).
95
+ * @param {string} downloadLink
96
+ * @param {string} fileName
97
+ * @param {string} [savePath] - Directory to save into (default: current working directory)
98
+ * @returns {Promise<string>} Absolute path to saved file
99
+ */
100
+ async downloadFileFromLink(downloadLink, fileName, savePath) {
101
+ const apiKey = this._client.configuration.apiKey;
102
+ const dir = savePath || '.';
103
+ await fs.promises.mkdir(dir, { recursive: true });
104
+ const filePath = path.join(dir, fileName);
105
+
106
+ const response = await axios.get(downloadLink, {
107
+ headers: { 'X-API-KEY': apiKey },
108
+ responseType: 'stream',
109
+ validateStatus: () => true,
110
+ });
111
+ if (response.status !== 200) {
112
+ throw new Error(`Failed to download file. Status code: ${response.status}`);
113
+ }
114
+ await new Promise((resolve, reject) => {
115
+ const ws = fs.createWriteStream(filePath);
116
+ response.data.pipe(ws);
117
+ response.data.on('error', reject);
118
+ ws.on('finish', () => resolve());
119
+ ws.on('error', reject);
120
+ });
121
+ return path.resolve(filePath);
122
+ }
123
+
124
+ /**
125
+ * GET /5.0/projects/{projectId}/file_areas/{fileAreaId}/files/{fileId}
126
+ * @param {string} projectId
127
+ * @param {string} fileAreaId
128
+ * @param {string} fileId
129
+ * @param {{ download?: boolean, savePath?: string }} [options] - If download is true, saves file and sets downloadedFilePath on the returned object
130
+ * @returns {Promise<object>}
131
+ */
132
+ async getFile(projectId, fileAreaId, fileId, options = {}) {
133
+ const fileInfo = await this._client.get(
134
+ `/5.0/projects/${projectId}/file_areas/${fileAreaId}/files/${fileId}`,
135
+ );
136
+ if (options.download && fileInfo && fileInfo.data && fileInfo.data.downloadLink) {
137
+ const name = fileInfo.data.fileName || fileId;
138
+ const downloadedPath = await this.downloadFileFromLink(
139
+ fileInfo.data.downloadLink,
140
+ name,
141
+ options.savePath,
142
+ );
143
+ fileInfo.downloadedFilePath = downloadedPath;
144
+ }
145
+ return fileInfo;
146
+ }
147
+
148
+ /**
149
+ * Download all files in a folder, with optional keyword / extension filters (Python parity).
150
+ * @param {string} projectId
151
+ * @param {string} fileAreaId
152
+ * @param {string} folderId
153
+ * @param {string} [savePath]
154
+ * @param {object} [opts] - filenameKeywords, filenameKeywordsMatch ('any'|'all'), filenameExtensions, params, verbose
155
+ * @returns {Promise<Array<{ fileName: string, downloadedFilePath: string }>>}
156
+ */
157
+ async bulkDownloadFolder(projectId, fileAreaId, folderId, savePath, opts = {}) {
158
+ if (typeof savePath === 'object' && savePath !== null && !Array.isArray(savePath)) {
159
+ opts = savePath;
160
+ savePath = undefined;
161
+ }
162
+ const {
163
+ filenameKeywords = null,
164
+ filenameKeywordsMatch = 'any',
165
+ filenameExtensions = null,
166
+ params = {},
167
+ verbose = false,
168
+ } = opts;
169
+
170
+ let files = await this.getAllFilesInFolder(projectId, fileAreaId, folderId, params, verbose);
171
+
172
+ if (filenameKeywords && filenameKeywords.length) {
173
+ const kws = filenameKeywords.map((kw) => String(kw).toLowerCase());
174
+ const matchFn =
175
+ filenameKeywordsMatch === 'all'
176
+ ? (name) => kws.every((kw) => name.includes(kw))
177
+ : (name) => kws.some((kw) => name.includes(kw));
178
+ files = files.filter((f) => {
179
+ const name = (((f.data) || {}).fileName) || '';
180
+ return matchFn(name.toLowerCase());
181
+ });
182
+ if (verbose) {
183
+ console.log(
184
+ `Files matching fileName keywords ${JSON.stringify(filenameKeywords)} (${filenameKeywordsMatch}): ${files.length}`,
185
+ );
186
+ }
187
+ }
188
+
189
+ if (filenameExtensions && filenameExtensions.length) {
190
+ const normExts = filenameExtensions.map((ext) =>
191
+ (ext.startsWith('.') ? ext : `.${ext}`).toLowerCase(),
192
+ );
193
+ const before = files.length;
194
+ files = files.filter((f) => {
195
+ const n = (((f.data) || {}).fileName) || ''.toLowerCase();
196
+ return normExts.some((ext) => n.endsWith(ext));
197
+ });
198
+ if (verbose) {
199
+ console.log(`Files matching fileName extensions ${JSON.stringify(normExts)}: ${files.length} / ${before}`);
200
+ }
201
+ }
202
+
203
+ const results = [];
204
+ for (let i = 0; i < files.length; i += 1) {
205
+ const f = files[i];
206
+ const data = f.data || {};
207
+ const fileName = data.fileName || data.fileId || `file_${i + 1}`;
208
+ const downloadLink = data.downloadLink;
209
+ if (!downloadLink) {
210
+ if (verbose) {
211
+ console.log(` [${i + 1}/${files.length}] Skipping '${fileName}' (no downloadLink)`);
212
+ }
213
+ continue;
214
+ }
215
+ if (verbose) {
216
+ console.log(` [${i + 1}/${files.length}] Downloading '${fileName}'...`);
217
+ }
218
+ const downloadedFilePath = await this.downloadFileFromLink(downloadLink, fileName, savePath);
219
+ results.push({ fileName, downloadedFilePath });
220
+ }
221
+ if (verbose) {
222
+ console.log(`Bulk download complete. ${results.length} file(s) downloaded.`);
223
+ }
224
+ return results;
225
+ }
226
+
227
+ /**
228
+ * Download files by id (fetches metadata per id then streams from downloadLink).
229
+ * @param {string} projectId
230
+ * @param {string} fileAreaId
231
+ * @param {string[]} fileIds
232
+ * @param {string} [savePath]
233
+ * @param {{ verbose?: boolean }} [options]
234
+ * @returns {Promise<Array<{ fileId: string, fileName: string, downloadedFilePath: string }>>}
235
+ */
236
+ async bulkDownloadByIds(projectId, fileAreaId, fileIds, savePath, options = {}) {
237
+ if (typeof savePath === 'object' && savePath !== null) {
238
+ options = savePath;
239
+ savePath = undefined;
240
+ }
241
+ const { verbose = false } = options;
242
+ const results = [];
243
+ const total = fileIds.length;
244
+ for (let i = 0; i < fileIds.length; i += 1) {
245
+ const fileId = fileIds[i];
246
+ const fileInfo = await this.getFile(projectId, fileAreaId, fileId);
247
+ const data = (fileInfo && fileInfo.data) || {};
248
+ const fileName = data.fileName || fileId;
249
+ const downloadLink = data.downloadLink;
250
+ if (!downloadLink) {
251
+ if (verbose) {
252
+ console.log(` [${i + 1}/${total}] Skipping '${fileName}' (no downloadLink)`);
253
+ }
254
+ continue;
255
+ }
256
+ if (verbose) {
257
+ console.log(` [${i + 1}/${total}] Downloading '${fileName}'...`);
258
+ }
259
+ const downloadedFilePath = await this.downloadFileFromLink(downloadLink, fileName, savePath);
260
+ results.push({ fileId, fileName, downloadedFilePath });
261
+ }
262
+ if (verbose) {
263
+ console.log(`Done. ${results.length}/${total} file(s) downloaded.`);
264
+ }
265
+ return results;
266
+ }
267
+
268
+ /**
269
+ * Retrieve properties mapping for a specific file.
270
+ * GET /1.0/projects/{projectId}/file_areas/{fileAreaId}/files/{fileId}/properties/1.0/mappings
271
+ * @param {string} projectId
272
+ * @param {string} fileAreaId
273
+ * @param {string} fileId
274
+ * @returns {Promise<object>}
275
+ */
276
+ getFilePropertiesMapping(projectId, fileAreaId, fileId) {
277
+ return this._client.get(
278
+ `/1.0/projects/${projectId}/file_areas/${fileAreaId}/files/${fileId}/properties/1.0/mappings`,
279
+ );
280
+ }
281
+
282
+ /**
283
+ * Retrieve valid property values for a specific file property mapping.
284
+ * GET /1.0/projects/{projectId}/file_areas/{fileAreaId}/files/properties/1.0/mappings/{filePropertyId}/values
285
+ * @param {string} projectId
286
+ * @param {string} fileAreaId
287
+ * @param {string} filePropertyId
288
+ * @returns {Promise<object>}
289
+ */
290
+ getFilePropertyMappingValues(projectId, fileAreaId, filePropertyId) {
291
+ return this._client.get(
292
+ `/1.0/projects/${projectId}/file_areas/${fileAreaId}/files/properties/1.0/mappings/${filePropertyId}/values`,
293
+ );
294
+ }
295
+ }
296
+
297
+ module.exports = FilesApi;
@@ -1,58 +1,58 @@
1
- 'use strict';
2
-
3
- /**
4
- * API methods for folders within a file area.
5
- */
6
- class FoldersApi {
7
- /**
8
- * @param {import('../apiClient')} apiClient
9
- */
10
- constructor(apiClient) {
11
- this._client = apiClient;
12
- }
13
-
14
- /**
15
- * Browse all folders on the given project and file area.
16
- * GET /5.1/projects/{projectId}/file_areas/{fileAreaId}/folders
17
- * @param {string} projectId
18
- * @param {string} fileAreaId
19
- * @param {object} [params]
20
- * @returns {Promise<object>}
21
- */
22
- listFolders(projectId, fileAreaId, params = {}) {
23
- return this._client.get(
24
- `/5.1/projects/${projectId}/file_areas/${fileAreaId}/folders`,
25
- params,
26
- );
27
- }
28
-
29
- /**
30
- * Retrieve a specific folder.
31
- * GET /5.0/projects/{projectId}/file_areas/{fileAreaId}/folders/{folderId}
32
- * @param {string} projectId
33
- * @param {string} fileAreaId
34
- * @param {string} folderId
35
- * @returns {Promise<object>}
36
- */
37
- getFolder(projectId, fileAreaId, folderId) {
38
- return this._client.get(
39
- `/5.0/projects/${projectId}/file_areas/${fileAreaId}/folders/${folderId}`,
40
- );
41
- }
42
-
43
- /**
44
- * Retrieve all properties for each file type in a specific folder.
45
- * GET /1.0/projects/{projectId}/file_areas/{fileAreaId}/folders/{folderId}/files/properties/1.0/mappings
46
- * @param {string} projectId
47
- * @param {string} fileAreaId
48
- * @param {string} folderId
49
- * @returns {Promise<object>}
50
- */
51
- getFolderFilesProperties(projectId, fileAreaId, folderId) {
52
- return this._client.get(
53
- `/1.0/projects/${projectId}/file_areas/${fileAreaId}/folders/${folderId}/files/properties/1.0/mappings`,
54
- );
55
- }
56
- }
57
-
58
- module.exports = FoldersApi;
1
+ 'use strict';
2
+
3
+ /**
4
+ * API methods for folders within a file area.
5
+ */
6
+ class FoldersApi {
7
+ /**
8
+ * @param {import('../apiClient')} apiClient
9
+ */
10
+ constructor(apiClient) {
11
+ this._client = apiClient;
12
+ }
13
+
14
+ /**
15
+ * Browse all folders on the given project and file area.
16
+ * GET /5.1/projects/{projectId}/file_areas/{fileAreaId}/folders
17
+ * @param {string} projectId
18
+ * @param {string} fileAreaId
19
+ * @param {object} [params]
20
+ * @returns {Promise<object>}
21
+ */
22
+ listFolders(projectId, fileAreaId, params = {}) {
23
+ return this._client.get(
24
+ `/5.1/projects/${projectId}/file_areas/${fileAreaId}/folders`,
25
+ params,
26
+ );
27
+ }
28
+
29
+ /**
30
+ * Retrieve a specific folder.
31
+ * GET /5.0/projects/{projectId}/file_areas/{fileAreaId}/folders/{folderId}
32
+ * @param {string} projectId
33
+ * @param {string} fileAreaId
34
+ * @param {string} folderId
35
+ * @returns {Promise<object>}
36
+ */
37
+ getFolder(projectId, fileAreaId, folderId) {
38
+ return this._client.get(
39
+ `/5.0/projects/${projectId}/file_areas/${fileAreaId}/folders/${folderId}`,
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Retrieve all properties for each file type in a specific folder.
45
+ * GET /1.0/projects/{projectId}/file_areas/{fileAreaId}/folders/{folderId}/files/properties/1.0/mappings
46
+ * @param {string} projectId
47
+ * @param {string} fileAreaId
48
+ * @param {string} folderId
49
+ * @returns {Promise<object>}
50
+ */
51
+ getFolderFilesProperties(projectId, fileAreaId, folderId) {
52
+ return this._client.get(
53
+ `/1.0/projects/${projectId}/file_areas/${fileAreaId}/folders/${folderId}/files/properties/1.0/mappings`,
54
+ );
55
+ }
56
+ }
57
+
58
+ module.exports = FoldersApi;