dalux-build-api 1.1.0 → 1.1.5
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 -21
- package/README.md +365 -365
- package/package.json +46 -46
- package/src/api/CompaniesApi.js +60 -60
- package/src/api/CompanyCatalogApi.js +125 -108
- package/src/api/FileAreasApi.js +55 -37
- package/src/api/FileRevisionsApi.js +31 -31
- package/src/api/FileUploadApi.js +64 -64
- package/src/api/FilesApi.js +697 -297
- package/src/api/FoldersApi.js +281 -58
- package/src/api/FormsApi.js +48 -48
- package/src/api/InspectionPlansApi.js +62 -62
- package/src/api/ProjectTemplatesApi.js +25 -25
- package/src/api/ProjectsApi.js +123 -106
- package/src/api/TasksApi.js +178 -161
- package/src/api/TestPlansApi.js +59 -59
- package/src/api/UsersApi.js +47 -47
- package/src/api/VersionSetsApi.js +67 -67
- package/src/api/WorkPackagesApi.js +26 -26
- package/src/apiClient.js +139 -75
- package/src/configuration.js +39 -24
- package/src/index.js +128 -92
- package/src/utils/errors.js +45 -0
- package/src/utils/index.js +26 -0
- package/src/utils/pagination.js +82 -0
- package/src/utils/pathResolver.js +124 -0
- package/src/utils/search.js +61 -0
- package/src/utils/validation.js +36 -0
package/src/api/ProjectsApi.js
CHANGED
|
@@ -1,106 +1,123 @@
|
|
|
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
|
-
|
|
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
|
+
* Get a project ID by its name.
|
|
107
|
+
* @param {string} projectName
|
|
108
|
+
* @returns {Promise<string|null>} The projectId, or null if not found.
|
|
109
|
+
*/
|
|
110
|
+
async getProjectByName(projectName) {
|
|
111
|
+
const { findByField } = require('../utils/search');
|
|
112
|
+
const response = await this.listProjects();
|
|
113
|
+
const items = (response && response.items) || [];
|
|
114
|
+
const project =
|
|
115
|
+
findByField(items, 'projectName', projectName, (x) => x.data || x) ||
|
|
116
|
+
findByField(items, 'name', projectName, (x) => x.data || x);
|
|
117
|
+
if (!project) return null;
|
|
118
|
+
const data = project.data || project;
|
|
119
|
+
return data.projectId || data.id || null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = ProjectsApi;
|
package/src/api/TasksApi.js
CHANGED
|
@@ -1,161 +1,178 @@
|
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
+
* Retrieve all task changes by following bookmark pagination automatically.
|
|
160
|
+
* @param {string} projectId
|
|
161
|
+
* @param {object} [params] - Optional query parameters (e.g. updatedAfter).
|
|
162
|
+
* @param {boolean} [verbose=false]
|
|
163
|
+
* @returns {Promise<object[]>} All task change items across all pages.
|
|
164
|
+
*/
|
|
165
|
+
async getAllProjectTaskChanges(projectId, params = {}, verbose = false) {
|
|
166
|
+
const { paginate } = require('../utils/pagination');
|
|
167
|
+
return paginate(
|
|
168
|
+
`/2.2/projects/${projectId}/tasks/changes`,
|
|
169
|
+
this._client,
|
|
170
|
+
params,
|
|
171
|
+
verbose,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
TasksApi.normalizeTaskParams = normalizeTaskParams;
|
|
177
|
+
|
|
178
|
+
module.exports = TasksApi;
|