From 8b34fd5ee0c10b085fc73de547c579ec8823f47e Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Mon, 25 Aug 2025 19:17:33 +0530 Subject: [PATCH 1/5] Added publish_all_localized flag in bulk publish/unpublish --- lib/stack/bulkOperation/index.js | 17 +- package-lock.json | 4 +- package.json | 2 +- test/sanity-check/api/bulkOperation-test.js | 306 ++++++++++++++++++++ types/stack/bulkOperation/index.d.ts | 2 + 5 files changed, 326 insertions(+), 5 deletions(-) diff --git a/lib/stack/bulkOperation/index.js b/lib/stack/bulkOperation/index.js index dc7c72eb..18fa9778 100644 --- a/lib/stack/bulkOperation/index.js +++ b/lib/stack/bulkOperation/index.js @@ -187,7 +187,7 @@ export function BulkOperation (http, data = {}) { * */ // eslint-disable-next-line camelcase - this.publish = async ({ details, skip_workflow_stage = false, approvals = false, is_nested = false, api_version = '' }) => { + this.publish = async ({ details, skip_workflow_stage = false, approvals = false, is_nested = false, api_version = '', publishAllLocalized = false }) => { var httpBody = {} if (details) { httpBody = cloneDeep(details) @@ -212,6 +212,12 @@ export function BulkOperation (http, data = {}) { if (approvals) { headers.headers.approvals = approvals } + if (publishAllLocalized) { + if (!headers.params) { + headers.params = {} + } + headers.params.publish_all_localized = publishAllLocalized + } // eslint-disable-next-line camelcase if (api_version) headers.headers.api_version = api_version @@ -279,7 +285,7 @@ export function BulkOperation (http, data = {}) { * .then((response) => { console.log(response.notice) }) */ // eslint-disable-next-line camelcase - this.unpublish = async ({ details, skip_workflow_stage = false, approvals = false, is_nested = false, api_version = '' }) => { + this.unpublish = async ({ details, skip_workflow_stage = false, approvals = false, is_nested = false, api_version = '', unpublishAllLocalized = false }) => { var httpBody = {} if (details) { httpBody = cloneDeep(details) @@ -306,6 +312,13 @@ export function BulkOperation (http, data = {}) { } // eslint-disable-next-line camelcase if (api_version) headers.headers.api_version = api_version + + if (unpublishAllLocalized) { + if (!headers.params) { + headers.params = {} + } + headers.params.publish_all_localized = unpublishAllLocalized + } return publishUnpublish(http, '/bulk/unpublish', httpBody, headers) } diff --git a/package-lock.json b/package-lock.json index fe2ccde9..52af398b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@contentstack/management", - "version": "1.24.0", + "version": "1.25.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@contentstack/management", - "version": "1.24.0", + "version": "1.25.0", "license": "MIT", "dependencies": { "assert": "^2.1.0", diff --git a/package.json b/package.json index 4fcc7f9b..2e6cb22f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/management", - "version": "1.24.0", + "version": "1.25.0", "description": "The Content Management API is used to manage the content of your Contentstack account", "main": "./dist/node/contentstack-management.js", "browser": "./dist/web/contentstack-management.js", diff --git a/test/sanity-check/api/bulkOperation-test.js b/test/sanity-check/api/bulkOperation-test.js index 8e33832d..92306848 100644 --- a/test/sanity-check/api/bulkOperation-test.js +++ b/test/sanity-check/api/bulkOperation-test.js @@ -16,6 +16,13 @@ let assetUid2 = '' let jobId1 = '' let jobId2 = '' let jobId3 = '' +let jobId4 = '' +let jobId5 = '' +let jobId6 = '' +let jobId7 = '' +let jobId8 = '' +let jobId9 = '' +let jobId10 = '' let tokenUidDev = '' let tokenUid = '' @@ -162,6 +169,228 @@ describe('BulkOperation api test', () => { .catch(done) }) + it('should publish entries with publishAllLocalized parameter set to true', done => { + const publishDetails = { + entries: [ + { + uid: entryUid1, + content_type: multiPageCT.content_type.uid, + locale: 'en-us' + } + ], + locales: [ + 'en-us' + ], + environments: [ + 'development' + ] + } + doBulkOperation() + .publish({ + details: publishDetails, + api_version: '3.2', + publishAllLocalized: true + }) + .then((response) => { + expect(response.notice).to.not.equal(undefined) + expect(response.job_id).to.not.equal(undefined) + // Store job ID for later status check + jobId4 = response.job_id + done() + }) + .catch(done) + }) + + it('should publish entries with publishAllLocalized parameter set to false', done => { + const publishDetails = { + entries: [ + { + uid: entryUid2, + content_type: singlepageCT.content_type.uid, + locale: 'en-us' + } + ], + locales: [ + 'en-us' + ], + environments: [ + 'development' + ] + } + doBulkOperation() + .publish({ + details: publishDetails, + api_version: '3.2', + publishAllLocalized: false + }) + .then((response) => { + expect(response.notice).to.not.equal(undefined) + expect(response.job_id).to.not.equal(undefined) + // Store job ID for later status check + jobId5 = response.job_id + done() + }) + .catch(done) + }) + + it('should publish assets with publishAllLocalized parameter', done => { + const publishDetails = { + assets: [ + { + uid: assetUid1 + } + ], + locales: [ + 'en-us' + ], + environments: [ + 'development' + ] + } + doBulkOperation() + .publish({ + details: publishDetails, + api_version: '3.2', + publishAllLocalized: true + }) + .then((response) => { + expect(response.notice).to.not.equal(undefined) + expect(response.job_id).to.not.equal(undefined) + // Store job ID for later status check + jobId6 = response.job_id + done() + }) + .catch(done) + }) + + it('should unpublish entries with unpublishAllLocalized parameter set to true', done => { + const unpublishDetails = { + entries: [ + { + uid: entryUid1, + content_type: multiPageCT.content_type.uid, + locale: 'en-us' + } + ], + locales: [ + 'en-us' + ], + environments: [ + 'development' + ] + } + doBulkOperation() + .unpublish({ + details: unpublishDetails, + api_version: '3.2', + unpublishAllLocalized: true + }) + .then((response) => { + expect(response.notice).to.not.equal(undefined) + expect(response.job_id).to.not.equal(undefined) + // Store job ID for later status check + jobId7 = response.job_id + done() + }) + .catch(done) + }) + + it('should unpublish entries with unpublishAllLocalized parameter set to false', done => { + const unpublishDetails = { + entries: [ + { + uid: entryUid2, + content_type: singlepageCT.content_type.uid, + locale: 'en-us' + } + ], + locales: [ + 'en-us' + ], + environments: [ + 'development' + ] + } + doBulkOperation() + .unpublish({ + details: unpublishDetails, + api_version: '3.2', + unpublishAllLocalized: false + }) + .then((response) => { + expect(response.notice).to.not.equal(undefined) + expect(response.job_id).to.not.equal(undefined) + // Store job ID for later status check + jobId8 = response.job_id + done() + }) + .catch(done) + }) + + it('should unpublish assets with unpublishAllLocalized parameter', done => { + const unpublishDetails = { + assets: [ + { + uid: assetUid1 + } + ], + locales: [ + 'en-us' + ], + environments: [ + 'development' + ] + } + doBulkOperation() + .unpublish({ + details: unpublishDetails, + api_version: '3.2', + unpublishAllLocalized: true + }) + .then((response) => { + expect(response.notice).to.not.equal(undefined) + expect(response.job_id).to.not.equal(undefined) + // Store job ID for later status check + jobId9 = response.job_id + done() + }) + .catch(done) + }) + + it('should publish entries with multiple parameters including publishAllLocalized', done => { + const publishDetails = { + entries: [ + { + uid: entryUid1, + content_type: multiPageCT.content_type.uid, + locale: 'en-us' + } + ], + locales: [ + 'en-us' + ], + environments: [ + 'development' + ] + } + doBulkOperation() + .publish({ + details: publishDetails, + api_version: '3.2', + publishAllLocalized: true, + skip_workflow_stage: true, + approvals: true + }) + .then((response) => { + expect(response.notice).to.not.equal(undefined) + expect(response.job_id).to.not.equal(undefined) + // Store job ID for later status check + jobId10 = response.job_id + done() + }) + .catch(done) + }) + it('should wait for all jobs to be processed before checking status', async () => { await delay(5000) // Wait 5 seconds for jobs to be processed }) @@ -216,6 +445,83 @@ describe('BulkOperation api test', () => { expect(response.body).to.not.equal(undefined) }) + it('should get job status for publishAllLocalized=true job', async () => { + const response = await waitForJobReady(jobId4) + + expect(response).to.not.equal(undefined) + expect(response.uid).to.not.equal(undefined) + expect(response.status).to.not.equal(undefined) + expect(response.action).to.not.equal(undefined) + expect(response.summary).to.not.equal(undefined) + expect(response.body).to.not.equal(undefined) + }) + + it('should get job status for publishAllLocalized=false job', async () => { + const response = await waitForJobReady(jobId5) + + expect(response).to.not.equal(undefined) + expect(response.uid).to.not.equal(undefined) + expect(response.status).to.not.equal(undefined) + expect(response.action).to.not.equal(undefined) + expect(response.summary).to.not.equal(undefined) + expect(response.body).to.not.equal(undefined) + }) + + it('should get job status for asset publishAllLocalized job', async () => { + const response = await waitForJobReady(jobId6) + + expect(response).to.not.equal(undefined) + expect(response.uid).to.not.equal(undefined) + expect(response.status).to.not.equal(undefined) + expect(response.action).to.not.equal(undefined) + expect(response.summary).to.not.equal(undefined) + expect(response.body).to.not.equal(undefined) + }) + + it('should get job status for unpublishAllLocalized=true job', async () => { + const response = await waitForJobReady(jobId7) + + expect(response).to.not.equal(undefined) + expect(response.uid).to.not.equal(undefined) + expect(response.status).to.not.equal(undefined) + expect(response.action).to.not.equal(undefined) + expect(response.summary).to.not.equal(undefined) + expect(response.body).to.not.equal(undefined) + }) + + it('should get job status for unpublishAllLocalized=false job', async () => { + const response = await waitForJobReady(jobId8) + + expect(response).to.not.equal(undefined) + expect(response.uid).to.not.equal(undefined) + expect(response.status).to.not.equal(undefined) + expect(response.action).to.not.equal(undefined) + expect(response.summary).to.not.equal(undefined) + expect(response.body).to.not.equal(undefined) + }) + + it('should get job status for asset unpublishAllLocalized job', async () => { + const response = await waitForJobReady(jobId9) + + expect(response).to.not.equal(undefined) + expect(response.uid).to.not.equal(undefined) + expect(response.status).to.not.equal(undefined) + expect(response.action).to.not.equal(undefined) + expect(response.summary).to.not.equal(undefined) + expect(response.body).to.not.equal(undefined) + }) + + it('should get job status for multiple parameters job', async () => { + const response = await waitForJobReady(jobId10) + + expect(response).to.not.equal(undefined) + expect(response.uid).to.not.equal(undefined) + expect(response.status).to.not.equal(undefined) + expect(response.action).to.not.equal(undefined) + expect(response.summary).to.not.equal(undefined) + expect(response.body).to.not.equal(undefined) + }) + it('should get job status with bulk_version parameter', async () => { await waitForJobReady(jobId1) diff --git a/types/stack/bulkOperation/index.d.ts b/types/stack/bulkOperation/index.d.ts index 268aa530..ba5c3731 100644 --- a/types/stack/bulkOperation/index.d.ts +++ b/types/stack/bulkOperation/index.d.ts @@ -17,6 +17,8 @@ export interface BulkOperationConfig { is_nested?: boolean api_version?: string bulk_version?: string + publishAllLocalized?: boolean + unpublishAllLocalized?: boolean } export interface PublishItems extends PublishDetails { From d31a26ba4401a0620baecf15def4ced775956576 Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Mon, 25 Aug 2025 19:20:48 +0530 Subject: [PATCH 2/5] Fixed lint errors --- test/sanity-check/api/bulkOperation-test.js | 40 ++++++++++----------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/test/sanity-check/api/bulkOperation-test.js b/test/sanity-check/api/bulkOperation-test.js index 92306848..4e1ccc02 100644 --- a/test/sanity-check/api/bulkOperation-test.js +++ b/test/sanity-check/api/bulkOperation-test.js @@ -186,10 +186,10 @@ describe('BulkOperation api test', () => { ] } doBulkOperation() - .publish({ - details: publishDetails, + .publish({ + details: publishDetails, api_version: '3.2', - publishAllLocalized: true + publishAllLocalized: true }) .then((response) => { expect(response.notice).to.not.equal(undefined) @@ -218,10 +218,10 @@ describe('BulkOperation api test', () => { ] } doBulkOperation() - .publish({ - details: publishDetails, + .publish({ + details: publishDetails, api_version: '3.2', - publishAllLocalized: false + publishAllLocalized: false }) .then((response) => { expect(response.notice).to.not.equal(undefined) @@ -248,10 +248,10 @@ describe('BulkOperation api test', () => { ] } doBulkOperation() - .publish({ - details: publishDetails, + .publish({ + details: publishDetails, api_version: '3.2', - publishAllLocalized: true + publishAllLocalized: true }) .then((response) => { expect(response.notice).to.not.equal(undefined) @@ -280,10 +280,10 @@ describe('BulkOperation api test', () => { ] } doBulkOperation() - .unpublish({ - details: unpublishDetails, + .unpublish({ + details: unpublishDetails, api_version: '3.2', - unpublishAllLocalized: true + unpublishAllLocalized: true }) .then((response) => { expect(response.notice).to.not.equal(undefined) @@ -312,10 +312,10 @@ describe('BulkOperation api test', () => { ] } doBulkOperation() - .unpublish({ - details: unpublishDetails, + .unpublish({ + details: unpublishDetails, api_version: '3.2', - unpublishAllLocalized: false + unpublishAllLocalized: false }) .then((response) => { expect(response.notice).to.not.equal(undefined) @@ -342,10 +342,10 @@ describe('BulkOperation api test', () => { ] } doBulkOperation() - .unpublish({ - details: unpublishDetails, + .unpublish({ + details: unpublishDetails, api_version: '3.2', - unpublishAllLocalized: true + unpublishAllLocalized: true }) .then((response) => { expect(response.notice).to.not.equal(undefined) @@ -374,8 +374,8 @@ describe('BulkOperation api test', () => { ] } doBulkOperation() - .publish({ - details: publishDetails, + .publish({ + details: publishDetails, api_version: '3.2', publishAllLocalized: true, skip_workflow_stage: true, From e05b7dff3108a52342e463db3a3d2bed77926695 Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Mon, 25 Aug 2025 19:24:28 +0530 Subject: [PATCH 3/5] Added changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78d146e3..2571992b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## [v1.25.0](https://github.com/contentstack/contentstack-management-javascript/tree/v1.25.0) (2025-09-03) + - Enhancement + - Added publish_all_localized param in bulk publish/unpublish + ## [v1.24.0](https://github.com/contentstack/contentstack-management-javascript/tree/v1.24.0) (2025-08-18) - Feat - Added Support for MFA From 0c59a6f27d74419572964abc6ddcc0e883547d22 Mon Sep 17 00:00:00 2001 From: Aravind Kumar Date: Mon, 8 Sep 2025 22:15:15 +0530 Subject: [PATCH 4/5] Delete secrets-scan.yml --- .github/workflows/secrets-scan.yml | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 .github/workflows/secrets-scan.yml diff --git a/.github/workflows/secrets-scan.yml b/.github/workflows/secrets-scan.yml deleted file mode 100644 index 049c02f4..00000000 --- a/.github/workflows/secrets-scan.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Secrets Scan -on: - pull_request: - types: [opened, synchronize, reopened] -jobs: - security-secrets: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: '2' - ref: '${{ github.event.pull_request.head.ref }}' - - run: | - git reset --soft HEAD~1 - - name: Install Talisman - run: | - # Download Talisman - wget https://github.com/thoughtworks/talisman/releases/download/v1.37.0/talisman_linux_amd64 -O talisman - - # Checksum verification - checksum=$(sha256sum ./talisman | awk '{print $1}') - if [ "$checksum" != "8e0ae8bb7b160bf10c4fa1448beb04a32a35e63505b3dddff74a092bccaaa7e4" ]; then exit 1; fi - - # Make it executable - chmod +x talisman - - name: Run talisman - run: | - # Run Talisman with the pre-commit hook - ./talisman --githook pre-commit \ No newline at end of file From b900b48ea7278cbda87de6c4b0870f18c26ddaa0 Mon Sep 17 00:00:00 2001 From: Aravind Kumar Date: Mon, 8 Sep 2025 22:15:19 +0530 Subject: [PATCH 5/5] Updated codeowners --- CODEOWNERS | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 1be7e0dc..0496bc6a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1,11 @@ -* @contentstack/security-admin +* @contentstack/devex-pr-reviewers + +.github/workflows/sca-scan.yml @contentstack/security-admin + +.github/workflows/codeql-anaylsis.yml @contentstack/security-admin + +**/.snyk @contentstack/security-admin + +.github/workflows/policy-scan.yml @contentstack/security-admin + +.github/workflows/issues-jira.yml @contentstack/security-admin