Skip to content

Commit c9bfea4

Browse files
enh: added support for get job item status
1 parent 661e3e3 commit c9bfea4

File tree

7 files changed

+257
-3
lines changed

7 files changed

+257
-3
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## [v1.27.6](https://github.com/contentstack/contentstack-management-javascript/tree/v1.27.6) (2026-03-02)
4+
- Enhancement
5+
- Added support for get job item status.
6+
37
## [v1.27.5](https://github.com/contentstack/contentstack-management-javascript/tree/v1.27.5) (2026-02-16)
48
- Enhancement
59
- OAuth PKCE: store code_verifier in sessionStorage (browser only) so token exchange works after redirect in React and other SPAs; verifier is restored on callback, cleared after successful exchange or on error; 10-minute expiry; Node remains memory-only

lib/stack/bulkOperation/index.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,49 @@ export function BulkOperation (http, data = {}) {
126126
}
127127
}
128128

129+
/**
130+
* The getJobItems request allows you to get the items of a bulk job.
131+
* Response structure varies based on query params: items (always), skip/limit/total_count (when include_count=true), and other fields per params.
132+
* @memberof BulkOperation
133+
* @func getJobItems
134+
* @returns {Promise<Object>} Response Object. Structure varies with params - always includes items array; may include skip, limit, total_count when include_count=true.
135+
* @param {String} job_id - The ID of the job.
136+
* @param {Object} [params={}] - Query parameters. Supports: include_count, skip, limit, include_reference, status, type, ct (content type UID or array), api_version, and any other dynamic query params.
137+
* @example
138+
* client.stack({ api_key: 'api_key'}).bulkOperation().getJobItems('job_id')
139+
* .then((response) => { console.log(response) })
140+
* @example
141+
* client.stack({ api_key: 'api_key'}).bulkOperation().getJobItems('job_id', { skip: 0, limit: 50, include_count: true })
142+
* .then((response) => { console.log(response) })
143+
*/
144+
// eslint-disable-next-line camelcase
145+
this.getJobItems = async (job_id, params = {}) => {
146+
// eslint-disable-next-line camelcase
147+
const { api_version = '3.2', ...queryParams } = cloneDeep(params)
148+
// eslint-disable-next-line camelcase
149+
this.urlPath = `/bulk/jobs/${job_id}/items`
150+
const headers = {
151+
headers: {
152+
...cloneDeep(this.stackHeaders)
153+
}
154+
}
155+
// eslint-disable-next-line camelcase
156+
if (api_version) headers.headers.api_version = api_version
157+
if (Object.keys(queryParams).length > 0) headers.params = queryParams
158+
try {
159+
const response = await http.get(this.urlPath, headers)
160+
if (response.data) {
161+
// eslint-disable-next-line camelcase
162+
if (api_version) delete headers.headers.api_version
163+
return response.data
164+
}
165+
} catch (error) {
166+
// eslint-disable-next-line camelcase
167+
if (api_version) delete headers.headers.api_version
168+
console.error(error)
169+
}
170+
}
171+
129172
/**
130173
* The Publish entries and assets in bulk request allows you to publish multiple entries and assets at the same time.
131174
* @memberof BulkOperation

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@contentstack/management",
3-
"version": "1.27.5",
3+
"version": "1.27.6",
44
"description": "The Content Management API is used to manage the content of your Contentstack account",
55
"main": "./dist/node/contentstack-management.js",
66
"browser": "./dist/web/contentstack-management.js",

test/sanity-check/api/bulkOperation-test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,26 @@ describe('BulkOperation api test', () => {
536536
expect(response.body).to.not.equal(undefined)
537537
})
538538

539+
it('should get job items for a completed job', async () => {
540+
await waitForJobReady(jobId1)
541+
542+
const response = await doBulkOperationWithManagementToken(tokenUidDev)
543+
.getJobItems(jobId1)
544+
545+
expect(response).to.not.equal(undefined)
546+
expect(response.items).to.be.an('array')
547+
})
548+
549+
it('should get job items with explicit api_version', async () => {
550+
await waitForJobReady(jobId2)
551+
552+
const response = await doBulkOperationWithManagementToken(tokenUidDev)
553+
.getJobItems(jobId2, { api_version: '3.2' })
554+
555+
expect(response).to.not.equal(undefined)
556+
expect(response.items).to.be.an('array')
557+
})
558+
539559
it('should delete a Management Token', done => {
540560
makeManagementToken(tokenUid)
541561
.delete()

test/unit/bulkOperation-test.js

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ describe('Contentstack BulkOperation test', () => {
1111
expect(bulkOperation.urlPath).to.be.equal('/bulk')
1212
expect(bulkOperation.stackHeaders).to.be.equal(undefined)
1313
expect(bulkOperation.addItems).to.not.equal(undefined)
14+
expect(bulkOperation.getJobItems).to.not.equal(undefined)
1415
expect(bulkOperation.publish).to.not.equal(undefined)
1516
expect(bulkOperation.unpublish).to.not.equal(undefined)
1617
expect(bulkOperation.delete).to.not.equal(undefined)
@@ -23,6 +24,7 @@ describe('Contentstack BulkOperation test', () => {
2324
expect(bulkOperation.stackHeaders).to.not.equal(undefined)
2425
expect(bulkOperation.stackHeaders.api_key).to.be.equal(stackHeadersMock.api_key)
2526
expect(bulkOperation.addItems).to.not.equal(undefined)
27+
expect(bulkOperation.getJobItems).to.not.equal(undefined)
2628
expect(bulkOperation.publish).to.not.equal(undefined)
2729
expect(bulkOperation.unpublish).to.not.equal(undefined)
2830
expect(bulkOperation.delete).to.not.equal(undefined)
@@ -218,6 +220,142 @@ describe('Contentstack BulkOperation test', () => {
218220
expect(response.notice).to.equal('Your job status request is successful.')
219221
expect(response.status).to.equal('completed')
220222
})
223+
224+
it('should fetch job items with default api_version', async () => {
225+
const jobId = 'job_id'
226+
227+
const mock = new MockAdapter(Axios)
228+
mock.onGet(`/bulk/jobs/${jobId}/items`).reply((config) => {
229+
expect(config.headers.api_version).to.equal('3.2')
230+
return [200, {
231+
items: [
232+
{ uid: 'entry_uid', content_type_uid: 'content_type_uid', status: 'completed' }
233+
]
234+
}]
235+
})
236+
237+
const response = await makeBulkOperation().getJobItems(jobId)
238+
expect(response.items).to.not.equal(undefined)
239+
expect(response.items).to.be.an('array')
240+
expect(response.items[0].uid).to.equal('entry_uid')
241+
expect(response.items[0].content_type_uid).to.equal('content_type_uid')
242+
})
243+
244+
it('should fetch job items with custom api_version', async () => {
245+
const jobId = 'job_id'
246+
const params = { api_version: '3.0' }
247+
248+
const mock = new MockAdapter(Axios)
249+
mock.onGet(`/bulk/jobs/${jobId}/items`).reply((config) => {
250+
expect(config.headers.api_version).to.equal('3.0')
251+
return [200, { items: [] }]
252+
})
253+
254+
const response = await makeBulkOperation().getJobItems(jobId, params)
255+
expect(response.items).to.not.equal(undefined)
256+
expect(response.items).to.be.an('array')
257+
})
258+
259+
it('should fetch job items with query params: include_count, skip, limit', async () => {
260+
const jobId = 'job_id'
261+
const params = {
262+
include_count: true,
263+
skip: 10,
264+
limit: 50
265+
}
266+
267+
const mock = new MockAdapter(Axios)
268+
mock.onGet(`/bulk/jobs/${jobId}/items`).reply((config) => {
269+
expect(config.params.include_count).to.equal(true)
270+
expect(config.params.skip).to.equal(10)
271+
expect(config.params.limit).to.equal(50)
272+
return [200, { items: [], count: 0 }]
273+
})
274+
275+
const response = await makeBulkOperation().getJobItems(jobId, params)
276+
expect(response.items).to.be.an('array')
277+
expect(response.count).to.equal(0)
278+
})
279+
280+
it('should fetch job items with query params: include_reference, status, type', async () => {
281+
const jobId = 'job_id'
282+
const params = {
283+
include_reference: false,
284+
status: 'failed',
285+
type: 'entry'
286+
}
287+
288+
const mock = new MockAdapter(Axios)
289+
mock.onGet(`/bulk/jobs/${jobId}/items`).reply((config) => {
290+
expect(config.params.include_reference).to.equal(false)
291+
expect(config.params.status).to.equal('failed')
292+
expect(config.params.type).to.equal('entry')
293+
return [200, { items: [] }]
294+
})
295+
296+
const response = await makeBulkOperation().getJobItems(jobId, params)
297+
expect(response.items).to.be.an('array')
298+
})
299+
300+
it('should fetch job items with ct (content type) filter as array', async () => {
301+
const jobId = 'job_id'
302+
const params = { ct: ['content_type_uid_1', 'content_type_uid_2'] }
303+
304+
const mock = new MockAdapter(Axios)
305+
mock.onGet(`/bulk/jobs/${jobId}/items`).reply((config) => {
306+
expect(config.params.ct).to.be.an('array')
307+
expect(config.params.ct).to.deep.equal(['content_type_uid_1', 'content_type_uid_2'])
308+
return [200, { items: [] }]
309+
})
310+
311+
const response = await makeBulkOperation().getJobItems(jobId, params)
312+
expect(response.items).to.be.an('array')
313+
})
314+
315+
it('should fetch job items with dynamic query params', async () => {
316+
const jobId = 'job_id'
317+
const params = {
318+
include_count: true,
319+
skip: 0,
320+
limit: 100,
321+
include_reference: true,
322+
status: 'success',
323+
type: 'asset',
324+
ct: ['blog_post', 'author'],
325+
custom_param: 'custom_value'
326+
}
327+
328+
const mock = new MockAdapter(Axios)
329+
mock.onGet(`/bulk/jobs/${jobId}/items`).reply((config) => {
330+
expect(config.params.include_count).to.equal(true)
331+
expect(config.params.skip).to.equal(0)
332+
expect(config.params.limit).to.equal(100)
333+
expect(config.params.include_reference).to.equal(true)
334+
expect(config.params.status).to.equal('success')
335+
expect(config.params.type).to.equal('asset')
336+
expect(config.params.ct).to.deep.equal(['blog_post', 'author'])
337+
expect(config.params.custom_param).to.equal('custom_value')
338+
return [200, { items: [], count: 0 }]
339+
})
340+
341+
const response = await makeBulkOperation().getJobItems(jobId, params)
342+
expect(response.items).to.be.an('array')
343+
expect(response.count).to.equal(0)
344+
})
345+
346+
it('should fetch job items with empty params object', async () => {
347+
const jobId = 'job_id'
348+
349+
const mock = new MockAdapter(Axios)
350+
mock.onGet(`/bulk/jobs/${jobId}/items`).reply((config) => {
351+
expect(config.headers.api_version).to.equal('3.2')
352+
expect(config.params).to.equal(undefined)
353+
return [200, { items: [] }]
354+
})
355+
356+
const response = await makeBulkOperation().getJobItems(jobId, {})
357+
expect(response.items).to.be.an('array')
358+
})
221359
})
222360

223361
function makeBulkOperation (data) {

types/stack/bulkOperation/index.d.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface BulkOperation extends SystemFields {
99
addItems(config: BulkAddItemsConfig): Promise<Response>
1010
updateItems(config: BulkAddItemsConfig): Promise<Response>
1111
jobStatus(config: BulkJobStatus): Promise<Response>
12+
getJobItems(job_id: string, params?: BulkJobItemsParams): Promise<BulkJobItemsResponse>
1213
}
1314
export interface BulkOperationConfig {
1415
details: PublishItems
@@ -61,4 +62,52 @@ export interface BulkJobStatus {
6162
job_id: AnyProperty;
6263
bulk_version?: string;
6364
api_version?: string;
65+
}
66+
67+
export interface BulkJobItemsParams {
68+
api_version?: string;
69+
include_count?: boolean;
70+
skip?: number;
71+
limit?: number;
72+
include_reference?: boolean;
73+
status?: string;
74+
type?: string;
75+
ct?: string | string[];
76+
[key: string]: unknown;
77+
}
78+
79+
export interface BulkJobItem {
80+
uid: string;
81+
locale: string;
82+
version: number;
83+
title: string;
84+
type: "asset" | "entry";
85+
publish_details: {
86+
status: string;
87+
failure_reason?: string;
88+
};
89+
publish_locale: string;
90+
environment: string;
91+
action: string;
92+
published_at: string | null;
93+
scheduled_at: string;
94+
user: string;
95+
depth: number;
96+
content_type?: { uid: string };
97+
}
98+
99+
/**
100+
* Response structure varies based on query params passed to getJobItems:
101+
* - items: Always present - array of job items
102+
* - skip, limit, total_count: Present when include_count=true
103+
* - Additional fields may be included based on other params (e.g. include_reference)
104+
*/
105+
export interface BulkJobItemsResponse {
106+
items?: Array<BulkJobItem | AnyProperty>;
107+
skip?: number;
108+
limit?: number;
109+
total_count?: number;
110+
count?: number;
111+
notice?: string;
112+
[key: string]: unknown;
64113
}

0 commit comments

Comments
 (0)