dalux-build-api 1.1.3 → 2.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.
Files changed (48) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +404 -365
  3. package/package.json +47 -46
  4. package/src/api/CompaniesApi.js +67 -60
  5. package/src/api/CompanyCatalogApi.js +129 -108
  6. package/src/api/FileAreasApi.js +57 -37
  7. package/src/api/FileRevisionsApi.js +31 -31
  8. package/src/api/FileUploadApi.js +64 -64
  9. package/src/api/FilesApi.js +701 -297
  10. package/src/api/FoldersApi.js +283 -58
  11. package/src/api/FormsApi.js +53 -48
  12. package/src/api/InspectionPlansApi.js +66 -62
  13. package/src/api/ProjectTemplatesApi.js +25 -25
  14. package/src/api/ProjectsApi.js +127 -106
  15. package/src/api/TasksApi.js +193 -161
  16. package/src/api/TestPlansApi.js +63 -59
  17. package/src/api/UsersApi.js +52 -47
  18. package/src/api/VersionSetsApi.js +75 -67
  19. package/src/api/WorkPackagesApi.js +30 -26
  20. package/src/apiClient.js +139 -75
  21. package/src/configuration.js +39 -24
  22. package/src/index.js +133 -92
  23. package/src/models/common.js +22 -0
  24. package/src/models/companies/index.js +14 -0
  25. package/src/models/companyCatalog/index.js +19 -0
  26. package/src/models/convert.js +44 -0
  27. package/src/models/fileAreas/index.js +20 -0
  28. package/src/models/fileRevisions/index.js +12 -0
  29. package/src/models/fileUpload/index.js +12 -0
  30. package/src/models/files/index.js +97 -0
  31. package/src/models/folders/index.js +20 -0
  32. package/src/models/forms/index.js +19 -0
  33. package/src/models/helpers.js +62 -0
  34. package/src/models/index.js +148 -0
  35. package/src/models/inspectionPlans/index.js +18 -0
  36. package/src/models/projectTemplates/index.js +11 -0
  37. package/src/models/projects/index.js +57 -0
  38. package/src/models/tasks/index.js +105 -0
  39. package/src/models/testPlans/index.js +17 -0
  40. package/src/models/users/index.js +29 -0
  41. package/src/models/versionSets/index.js +22 -0
  42. package/src/models/workPackages/index.js +19 -0
  43. package/src/utils/errors.js +45 -0
  44. package/src/utils/index.js +26 -0
  45. package/src/utils/pagination.js +82 -0
  46. package/src/utils/pathResolver.js +124 -0
  47. package/src/utils/search.js +61 -0
  48. package/src/utils/validation.js +36 -0
@@ -1,106 +1,127 @@
1
- 'use strict';
2
-
3
- /**
4
- * API methods for project management.
5
- */
6
- class ProjectsApi {
7
- /**
8
- * @param {import('../apiClient')} apiClient
9
- */
10
- constructor(apiClient) {
11
- this._client = apiClient;
12
- }
13
-
14
- /**
15
- * Get all available projects.
16
- * GET /5.1/projects
17
- * @param {object} [params] - Optional query parameters (e.g. updatedAfter)
18
- * @returns {Promise<object>}
19
- */
20
- listProjects(params = {}) {
21
- return this._client.get('/5.1/projects', params);
22
- }
23
-
24
- /**
25
- * Get a specific project.
26
- * GET /5.0/projects/{projectId}
27
- * @param {string} projectId
28
- * @returns {Promise<object>}
29
- */
30
- getProject(projectId) {
31
- return this._client.get(`/5.0/projects/${projectId}`);
32
- }
33
-
34
- /**
35
- * Create a new project.
36
- * POST /5.0/projects
37
- * @param {object} body
38
- * @returns {Promise<object>}
39
- */
40
- createProject(body) {
41
- return this._client.post('/5.0/projects', body);
42
- }
43
-
44
- /**
45
- * Update a project.
46
- * PATCH /5.0/projects/{projectId}
47
- * @param {string} projectId
48
- * @param {object} body
49
- * @returns {Promise<object>}
50
- */
51
- updateProject(projectId, body) {
52
- return this._client.patch(`/5.0/projects/${projectId}`, body);
53
- }
54
-
55
- /**
56
- * Get all metadata available for POST project operations.
57
- * GET /1.0/projects/metadata/1.0/mappings
58
- * @returns {Promise<object>}
59
- */
60
- listMetadataMappingsForProjects() {
61
- return this._client.get('/1.0/projects/metadata/1.0/mappings');
62
- }
63
-
64
- /**
65
- * Get available values for a metadata key in POST project operations.
66
- * GET /1.0/projects/metadata/1.0/mappings/{key}/values
67
- * @param {string} key
68
- * @returns {Promise<object>}
69
- */
70
- listMetadataValuesForProjects(key) {
71
- return this._client.get(`/1.0/projects/metadata/1.0/mappings/${key}/values`);
72
- }
73
-
74
- /**
75
- * Get metadata of a specific project.
76
- * GET /1.0/projects/{projectId}/metadata
77
- * @param {string} projectId
78
- * @returns {Promise<object>}
79
- */
80
- listProjectMetadata(projectId) {
81
- return this._client.get(`/1.0/projects/${projectId}/metadata`);
82
- }
83
-
84
- /**
85
- * Get all metadata available for PATCH project operations.
86
- * GET /1.0/projects/{projectId}/metadata/1.0/mappings
87
- * @param {string} projectId
88
- * @returns {Promise<object>}
89
- */
90
- listProjectMetadataMappings(projectId) {
91
- return this._client.get(`/1.0/projects/${projectId}/metadata/1.0/mappings`);
92
- }
93
-
94
- /**
95
- * Get available values for metadata in a PATCH project operation.
96
- * GET /1.0/projects/{projectId}/metadata/1.0/mappings/{key}/values
97
- * @param {string} projectId
98
- * @param {string} key
99
- * @returns {Promise<object>}
100
- */
101
- listProjectMetadataValues(projectId, key) {
102
- return this._client.get(`/1.0/projects/${projectId}/metadata/1.0/mappings/${key}/values`);
103
- }
104
- }
105
-
106
- module.exports = ProjectsApi;
1
+ 'use strict';
2
+
3
+ const { findByField } = require('../utils/search');
4
+ const { convertToModel } = require('../models/convert');
5
+ const { ProjectsListResponseSchema, ProjectResponseSchema } = require('../models/projects');
6
+
7
+ /**
8
+ * API methods for project management.
9
+ */
10
+ class ProjectsApi {
11
+ /**
12
+ * @param {import('../apiClient')} apiClient
13
+ */
14
+ constructor(apiClient) {
15
+ this._client = apiClient;
16
+ }
17
+
18
+ /**
19
+ * Get all available projects.
20
+ * GET /5.1/projects
21
+ * @param {object} [params] - Optional query parameters (e.g. updatedAfter)
22
+ * @returns {Promise<object>} ProjectsListResponse ({ items: Project[], metadata?, links? })
23
+ */
24
+ async listProjects(params = {}) {
25
+ const response = await this._client.get('/5.1/projects', params);
26
+ return convertToModel(response, ProjectsListResponseSchema, 'ProjectsListResponse');
27
+ }
28
+
29
+ /**
30
+ * Get a specific project.
31
+ * GET /5.0/projects/{projectId}
32
+ * @param {string} projectId
33
+ * @returns {Promise<object>} ProjectResponse ({ data: Project, links? })
34
+ */
35
+ async getProject(projectId) {
36
+ const response = await this._client.get(`/5.0/projects/${projectId}`);
37
+ return convertToModel(response, ProjectResponseSchema, 'ProjectResponse');
38
+ }
39
+
40
+ /**
41
+ * Create a new project.
42
+ * POST /5.0/projects
43
+ * @param {object} body
44
+ * @returns {Promise<object>} ProjectResponse
45
+ */
46
+ async createProject(body) {
47
+ const response = await this._client.post('/5.0/projects', body);
48
+ return convertToModel(response, ProjectResponseSchema, 'ProjectResponse');
49
+ }
50
+
51
+ /**
52
+ * Update a project.
53
+ * PATCH /5.0/projects/{projectId}
54
+ * @param {string} projectId
55
+ * @param {object} body
56
+ * @returns {Promise<object>} ProjectResponse
57
+ */
58
+ async updateProject(projectId, body) {
59
+ const response = await this._client.patch(`/5.0/projects/${projectId}`, body);
60
+ return convertToModel(response, ProjectResponseSchema, 'ProjectResponse');
61
+ }
62
+
63
+ /**
64
+ * Get all metadata available for POST project operations.
65
+ * GET /1.0/projects/metadata/1.0/mappings
66
+ * @returns {Promise<object>}
67
+ */
68
+ listMetadataMappingsForProjects() {
69
+ return this._client.get('/1.0/projects/metadata/1.0/mappings');
70
+ }
71
+
72
+ /**
73
+ * Get available values for a metadata key in POST project operations.
74
+ * GET /1.0/projects/metadata/1.0/mappings/{key}/values
75
+ * @param {string} key
76
+ * @returns {Promise<object>}
77
+ */
78
+ listMetadataValuesForProjects(key) {
79
+ return this._client.get(`/1.0/projects/metadata/1.0/mappings/${key}/values`);
80
+ }
81
+
82
+ /**
83
+ * Get metadata of a specific project.
84
+ * GET /1.0/projects/{projectId}/metadata
85
+ * @param {string} projectId
86
+ * @returns {Promise<object>}
87
+ */
88
+ listProjectMetadata(projectId) {
89
+ return this._client.get(`/1.0/projects/${projectId}/metadata`);
90
+ }
91
+
92
+ /**
93
+ * Get all metadata available for PATCH project operations.
94
+ * GET /1.0/projects/{projectId}/metadata/1.0/mappings
95
+ * @param {string} projectId
96
+ * @returns {Promise<object>}
97
+ */
98
+ listProjectMetadataMappings(projectId) {
99
+ return this._client.get(`/1.0/projects/${projectId}/metadata/1.0/mappings`);
100
+ }
101
+
102
+ /**
103
+ * Get available values for metadata in a PATCH project operation.
104
+ * GET /1.0/projects/{projectId}/metadata/1.0/mappings/{key}/values
105
+ * @param {string} projectId
106
+ * @param {string} key
107
+ * @returns {Promise<object>}
108
+ */
109
+ listProjectMetadataValues(projectId, key) {
110
+ return this._client.get(`/1.0/projects/${projectId}/metadata/1.0/mappings/${key}/values`);
111
+ }
112
+
113
+ /**
114
+ * Get a project ID by its name.
115
+ * @param {string} projectName
116
+ * @returns {Promise<string|null>} The projectId, or null if not found.
117
+ */
118
+ async getProjectByName(projectName) {
119
+ const response = await this.listProjects();
120
+ const items = (response && response.items) || [];
121
+ const project = findByField(items, 'projectName', projectName);
122
+ if (!project) return null;
123
+ return project.projectId || null;
124
+ }
125
+ }
126
+
127
+ module.exports = ProjectsApi;
@@ -1,161 +1,193 @@
1
- 'use strict';
2
-
3
- /**
4
- * Build query params for GET /5.2/projects/.../tasks (OData).
5
- * If params contains typeId and no $filter, expands to
6
- * $filter=data/type/typeId eq '<typeId>'
7
- * Single quotes in typeId are escaped as '' per OData. If $filter is set,
8
- * typeId is still omitted from the outgoing query (not merged).
9
- * @param {object} [params]
10
- * @returns {object}
11
- */
12
- function normalizeTaskParams(params = {}) {
13
- const normalized = { ...params };
14
- const typeId = normalized.typeId;
15
- delete normalized.typeId;
16
- if (typeId != null && normalized.$filter === undefined) {
17
- const escaped = String(typeId).replace(/'/g, "''");
18
- normalized.$filter = `data/type/typeId eq '${escaped}'`;
19
- }
20
- return normalized;
21
- }
22
-
23
- /**
24
- * API methods for tasks, approvals, safety issues, observations and good practices.
25
- */
26
- class TasksApi {
27
- /**
28
- * @param {import('../apiClient')} apiClient
29
- */
30
- constructor(apiClient) {
31
- this._client = apiClient;
32
- }
33
-
34
- /**
35
- * Retrieve tasks, approvals, safety issues, safety observations and good practices on a project.
36
- * GET /5.2/projects/{projectId}/tasks
37
- * @param {string} projectId
38
- * @param {object} [params] - Optional filters (e.g. updatedAfter). Pass typeId as shorthand for
39
- * OData $filter on task type, or pass $filter (and other OData options) directly.
40
- * @returns {Promise<object>}
41
- */
42
- getProjectTasks(projectId, params = {}) {
43
- return this._client.get(`/5.2/projects/${projectId}/tasks`, normalizeTaskParams(params));
44
- }
45
-
46
- /**
47
- * Retrieve all tasks on a project by following bookmark pagination automatically.
48
- * Matches Python client behaviour: uses metadata.totalRemainingItems when present;
49
- * otherwise uses metadata.totalItems across pages as a ceiling so pagination cannot run forever.
50
- * @param {string} projectId
51
- * @param {object} [params] - Optional filters / OData (typeId shorthand supported).
52
- * @param {boolean} [verbose=false] - Log progress to console.
53
- * @returns {Promise<object[]>} All task items across all pages
54
- */
55
- async getAllProjectTasks(projectId, params = {}, verbose = false) {
56
- const allItems = [];
57
- const baseParams = normalizeTaskParams(params);
58
- let currentParams = { ...baseParams };
59
- let hasNextPage = true;
60
- /** @type {number|null} */
61
- let tasksItemsCeiling = null;
62
-
63
- while (hasNextPage) {
64
- const response = await this._client.get(`/5.2/projects/${projectId}/tasks`, currentParams);
65
- const items = Array.isArray(response.items) ? response.items : [];
66
- if (items.length) {
67
- allItems.push(...items);
68
- }
69
- const meta = (response && response.metadata) || {};
70
- let remaining;
71
- let useFilesRemainingStop;
72
-
73
- if (Object.prototype.hasOwnProperty.call(meta, 'totalRemainingItems')) {
74
- remaining = Number(meta.totalRemainingItems);
75
- useFilesRemainingStop = true;
76
- } else if (Object.prototype.hasOwnProperty.call(meta, 'totalItems')) {
77
- const ti = Number(meta.totalItems);
78
- tasksItemsCeiling = Math.max(tasksItemsCeiling || 0, ti);
79
- remaining = ti;
80
- useFilesRemainingStop = false;
81
- } else {
82
- remaining = 0;
83
- useFilesRemainingStop = true;
84
- }
85
-
86
- const nextLink = (response.links || []).find((l) => l.rel === 'nextPage');
87
- const nextHref = nextLink ? nextLink.href : null;
88
-
89
- if (verbose) {
90
- const nextPart = nextHref ? ` next: ${nextHref}` : ' next: (none)';
91
- if (useFilesRemainingStop) {
92
- console.log(`Retrieved ${allItems.length} tasks so far, ${remaining} remaining...${nextPart}`);
93
- } else if (tasksItemsCeiling != null) {
94
- const remV = Math.max(0, tasksItemsCeiling - allItems.length);
95
- console.log(`Retrieved ${allItems.length} tasks so far, ${remV} remaining...${nextPart}`);
96
- } else {
97
- console.log(`Retrieved ${allItems.length} tasks so far, ${remaining} remaining...${nextPart}`);
98
- }
99
- }
100
-
101
- if (!items.length) {
102
- hasNextPage = false;
103
- } else if (useFilesRemainingStop && remaining === 0) {
104
- hasNextPage = false;
105
- } else if (
106
- !useFilesRemainingStop &&
107
- tasksItemsCeiling != null &&
108
- allItems.length >= tasksItemsCeiling
109
- ) {
110
- hasNextPage = false;
111
- } else if (nextLink) {
112
- const bookmark = new URL(nextLink.href).searchParams.get('bookmark');
113
- currentParams = { ...baseParams, bookmark };
114
- } else {
115
- hasNextPage = false;
116
- }
117
- }
118
-
119
- if (verbose) {
120
- console.log(`Done. Total tasks retrieved: ${allItems.length}`);
121
- }
122
- return allItems;
123
- }
124
-
125
- /**
126
- * Retrieve a specific task/approval/safety issue/safety observation/good practice.
127
- * GET /3.3/projects/{projectId}/tasks/{taskId}
128
- * @param {string} projectId
129
- * @param {string} taskId
130
- * @returns {Promise<object>}
131
- */
132
- getTask(projectId, taskId) {
133
- return this._client.get(`/3.3/projects/${projectId}/tasks/${taskId}`);
134
- }
135
-
136
- /**
137
- * Retrieve task changes on a project in incremental updates.
138
- * GET /2.2/projects/{projectId}/tasks/changes
139
- * @param {string} projectId
140
- * @param {object} [params] - Optional filters (e.g. updatedAfter)
141
- * @returns {Promise<object>}
142
- */
143
- getProjectTaskChanges(projectId, params = {}) {
144
- return this._client.get(`/2.2/projects/${projectId}/tasks/changes`, params);
145
- }
146
-
147
- /**
148
- * Retrieve attachments on tasks on a project.
149
- * GET /1.1/projects/{projectId}/tasks/attachments
150
- * @param {string} projectId
151
- * @param {object} [params] - Optional filters (e.g. updatedAfter)
152
- * @returns {Promise<object>}
153
- */
154
- getProjectTaskAttachments(projectId, params = {}) {
155
- return this._client.get(`/1.1/projects/${projectId}/tasks/attachments`, params);
156
- }
157
- }
158
-
159
- TasksApi.normalizeTaskParams = normalizeTaskParams;
160
-
161
- module.exports = TasksApi;
1
+ 'use strict';
2
+
3
+ const { convertToModel, convertToModelList } = require('../models/convert');
4
+ const {
5
+ TaskSchema,
6
+ TaskChangeSchema,
7
+ TasksListResponseSchema,
8
+ TaskResponseSchema,
9
+ TaskChangesSchema,
10
+ TaskAttachmentsListResponseSchema,
11
+ } = require('../models/tasks');
12
+
13
+ /**
14
+ * Build query params for GET /5.2/projects/.../tasks (OData).
15
+ * If params contains typeId and no $filter, expands to
16
+ * $filter=data/type/typeId eq '<typeId>'
17
+ * Single quotes in typeId are escaped as '' per OData. If $filter is set,
18
+ * typeId is still omitted from the outgoing query (not merged).
19
+ * @param {object} [params]
20
+ * @returns {object}
21
+ */
22
+ function normalizeTaskParams(params = {}) {
23
+ const normalized = { ...params };
24
+ const typeId = normalized.typeId;
25
+ delete normalized.typeId;
26
+ if (typeId != null && normalized.$filter === undefined) {
27
+ const escaped = String(typeId).replace(/'/g, "''");
28
+ normalized.$filter = `data/type/typeId eq '${escaped}'`;
29
+ }
30
+ return normalized;
31
+ }
32
+
33
+ /**
34
+ * API methods for tasks, approvals, safety issues, observations and good practices.
35
+ */
36
+ class TasksApi {
37
+ /**
38
+ * @param {import('../apiClient')} apiClient
39
+ */
40
+ constructor(apiClient) {
41
+ this._client = apiClient;
42
+ }
43
+
44
+ /**
45
+ * Retrieve tasks, approvals, safety issues, safety observations and good practices on a project.
46
+ * GET /5.2/projects/{projectId}/tasks
47
+ * @param {string} projectId
48
+ * @param {object} [params] - Optional filters (e.g. updatedAfter). Pass typeId as shorthand for
49
+ * OData $filter on task type, or pass $filter (and other OData options) directly.
50
+ * @returns {Promise<object>}
51
+ */
52
+ async getProjectTasks(projectId, params = {}) {
53
+ const response = await this._client.get(`/5.2/projects/${projectId}/tasks`, normalizeTaskParams(params));
54
+ return convertToModel(response, TasksListResponseSchema, 'TasksListResponse');
55
+ }
56
+
57
+ /**
58
+ * Retrieve all tasks on a project by following bookmark pagination automatically.
59
+ * Matches Python client behaviour: uses metadata.totalRemainingItems when present;
60
+ * otherwise uses metadata.totalItems across pages as a ceiling so pagination cannot run forever.
61
+ * @param {string} projectId
62
+ * @param {object} [params] - Optional filters / OData (typeId shorthand supported).
63
+ * @param {boolean} [verbose=false] - Log progress to console.
64
+ * @returns {Promise<object[]>} All task items across all pages
65
+ */
66
+ async getAllProjectTasks(projectId, params = {}, verbose = false) {
67
+ const allItems = [];
68
+ const baseParams = normalizeTaskParams(params);
69
+ let currentParams = { ...baseParams };
70
+ let hasNextPage = true;
71
+ /** @type {number|null} */
72
+ let tasksItemsCeiling = null;
73
+
74
+ while (hasNextPage) {
75
+ const response = await this._client.get(`/5.2/projects/${projectId}/tasks`, currentParams);
76
+ const items = Array.isArray(response.items) ? response.items : [];
77
+ if (items.length) {
78
+ allItems.push(...items);
79
+ }
80
+ const meta = (response && response.metadata) || {};
81
+ let remaining;
82
+ let useFilesRemainingStop;
83
+
84
+ if (Object.prototype.hasOwnProperty.call(meta, 'totalRemainingItems')) {
85
+ remaining = Number(meta.totalRemainingItems);
86
+ useFilesRemainingStop = true;
87
+ } else if (Object.prototype.hasOwnProperty.call(meta, 'totalItems')) {
88
+ const ti = Number(meta.totalItems);
89
+ tasksItemsCeiling = Math.max(tasksItemsCeiling || 0, ti);
90
+ remaining = ti;
91
+ useFilesRemainingStop = false;
92
+ } else {
93
+ remaining = 0;
94
+ useFilesRemainingStop = true;
95
+ }
96
+
97
+ const nextLink = (response.links || []).find((l) => l.rel === 'nextPage');
98
+ const nextHref = nextLink ? nextLink.href : null;
99
+
100
+ if (verbose) {
101
+ const nextPart = nextHref ? ` next: ${nextHref}` : ' next: (none)';
102
+ if (useFilesRemainingStop) {
103
+ console.log(`Retrieved ${allItems.length} tasks so far, ${remaining} remaining...${nextPart}`);
104
+ } else if (tasksItemsCeiling != null) {
105
+ const remV = Math.max(0, tasksItemsCeiling - allItems.length);
106
+ console.log(`Retrieved ${allItems.length} tasks so far, ${remV} remaining...${nextPart}`);
107
+ } else {
108
+ console.log(`Retrieved ${allItems.length} tasks so far, ${remaining} remaining...${nextPart}`);
109
+ }
110
+ }
111
+
112
+ if (!items.length) {
113
+ hasNextPage = false;
114
+ } else if (useFilesRemainingStop && remaining === 0) {
115
+ hasNextPage = false;
116
+ } else if (
117
+ !useFilesRemainingStop &&
118
+ tasksItemsCeiling != null &&
119
+ allItems.length >= tasksItemsCeiling
120
+ ) {
121
+ hasNextPage = false;
122
+ } else if (nextLink) {
123
+ const bookmark = new URL(nextLink.href).searchParams.get('bookmark');
124
+ currentParams = { ...baseParams, bookmark };
125
+ } else {
126
+ hasNextPage = false;
127
+ }
128
+ }
129
+
130
+ if (verbose) {
131
+ console.log(`Done. Total tasks retrieved: ${allItems.length}`);
132
+ }
133
+ return convertToModelList(allItems, TaskSchema, 'Task');
134
+ }
135
+
136
+ /**
137
+ * Retrieve a specific task/approval/safety issue/safety observation/good practice.
138
+ * GET /3.3/projects/{projectId}/tasks/{taskId}
139
+ * @param {string} projectId
140
+ * @param {string} taskId
141
+ * @returns {Promise<object>}
142
+ */
143
+ async getTask(projectId, taskId) {
144
+ const response = await this._client.get(`/3.3/projects/${projectId}/tasks/${taskId}`);
145
+ return convertToModel(response, TaskResponseSchema, 'TaskResponse');
146
+ }
147
+
148
+ /**
149
+ * Retrieve task changes on a project in incremental updates.
150
+ * GET /2.2/projects/{projectId}/tasks/changes
151
+ * @param {string} projectId
152
+ * @param {object} [params] - Optional filters (e.g. updatedAfter)
153
+ * @returns {Promise<object>}
154
+ */
155
+ async getProjectTaskChanges(projectId, params = {}) {
156
+ const response = await this._client.get(`/2.2/projects/${projectId}/tasks/changes`, params);
157
+ return convertToModel(response, TaskChangesSchema, 'TaskChanges');
158
+ }
159
+
160
+ /**
161
+ * Retrieve attachments on tasks on a project.
162
+ * GET /1.1/projects/{projectId}/tasks/attachments
163
+ * @param {string} projectId
164
+ * @param {object} [params] - Optional filters (e.g. updatedAfter)
165
+ * @returns {Promise<object>}
166
+ */
167
+ async getProjectTaskAttachments(projectId, params = {}) {
168
+ const response = await this._client.get(`/1.1/projects/${projectId}/tasks/attachments`, params);
169
+ return convertToModel(response, TaskAttachmentsListResponseSchema, 'TaskAttachmentsListResponse');
170
+ }
171
+
172
+ /**
173
+ * Retrieve all task changes by following bookmark pagination automatically.
174
+ * @param {string} projectId
175
+ * @param {object} [params] - Optional query parameters (e.g. updatedAfter).
176
+ * @param {boolean} [verbose=false]
177
+ * @returns {Promise<object[]>} All task change items across all pages.
178
+ */
179
+ async getAllProjectTaskChanges(projectId, params = {}, verbose = false) {
180
+ const { paginate } = require('../utils/pagination');
181
+ const raw = await paginate(
182
+ `/2.2/projects/${projectId}/tasks/changes`,
183
+ this._client,
184
+ params,
185
+ verbose,
186
+ );
187
+ return convertToModelList(raw, TaskChangeSchema, 'TaskChange');
188
+ }
189
+ }
190
+
191
+ TasksApi.normalizeTaskParams = normalizeTaskParams;
192
+
193
+ module.exports = TasksApi;