Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
1a3d370
Added branch support in entry variants
sunil-lakshman May 13, 2026
32508ea
Added publish and unpublish methods in entry variants
sunil-lakshman May 14, 2026
adf5d21
feat: add Asset Scan Status integration tests to sanity suite
aniket-shikhare-cstk Jun 25, 2026
ad9bf7d
feat: comprehensive asset scan status integration tests (non-AM + AM …
aniket-shikhare-cstk Jun 25, 2026
b0972f7
feat: add design-doc-driven scan tests for §3.2, §3.3, §3.4, §3.6, §4.2
aniket-shikhare-cstk Jun 25, 2026
7f7e44b
fix: correct chai have.property assertions — drop second arg used as …
aniket-shikhare-cstk Jun 25, 2026
a9eafb8
refactor: dynamically create AM org stack instead of requiring static…
aniket-shikhare-cstk Jun 25, 2026
be7b317
fix: create temporary environment for publish tests instead of skipping
aniket-shikhare-cstk Jun 25, 2026
e2f43f3
feat: add scan lifecycle tests for clean + quarantined status using E…
aniket-shikhare-cstk Jun 26, 2026
190e143
feat: enhance job readiness checks and implement retry logic for test…
aniket-shikhare-cstk Jun 28, 2026
9150bde
fix: resolve ESLint no-multi-spaces violations in assetScanStatus-tes…
aniket-shikhare-cstk Jun 28, 2026
b2629ee
Merge pull request #570 from contentstack/master
reeshika-h Jul 6, 2026
cafea65
Merge branch 'development' into enh/dx-7264
reeshika-h Jul 20, 2026
8d966ca
chore: update version to 1.31.0 and enhance changelog with entry vari…
reeshika-h Jul 21, 2026
d6f2610
Merge remote-tracking branch 'origin/master' into feat/asset-scan-sta…
aniket-shikhare-cstk Jul 21, 2026
fb7bdbc
fix: address Copilot review — download assertions, EICAR gate, talism…
aniket-shikhare-cstk Jul 21, 2026
3365429
Merge pull request #567 from contentstack/feat/asset-scan-status-sani…
aniket-shikhare-cstk Jul 21, 2026
48ebc7f
Merge pull request #556 from contentstack/enh/dx-7264
OMpawar-21 Jul 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .talismanrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
fileignoreconfig:
- filename: package-lock.json
checksum: 80c8eb5270a1c4a0fa244d0f001d15867449d44954eb4d3f1ab7fa68c6f5446d
version: ""
- filename: test/sanity-check/api/assetScanStatus-test.js
checksum: e3b1857fbe321e7125b55613acd58c3c0b2fd2462e7a14e48279ad35db4169e7
version: "1.0"
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## [v1.31.0](https://github.com/contentstack/contentstack-management-javascript/tree/v1.31.0) (2026-07-27)

- Enh
- Entry variants: `contentType(...).entry(...).variants(variantUidOrUids, branchName?)` — optional second argument sets the CMA `branch` header for that variants scope (branch UID or alias). First argument accepts a variant UID string or an array of UIDs (comma-separated in the request path). Omitting `branchName` preserves previous behavior.
- Entry variants: added `variants(uid).publish()` and `variants(uid).unpublish()`, calling the entry publish/unpublish endpoints with the variant payload nested under `entry`.
- `publish()`/`unpublish()` (entry and entry variants) accept optional `headers` and `params`, merged into the underlying HTTP request.
- Test
- Unit tests and sanity API tests for entry variants with an explicit branch, and for the new variant `publish()`/`unpublish()` methods.

## [v1.30.4](https://github.com/contentstack/contentstack-management-javascript/tree/v1.30.4) (2026-06-29)

- Update dependencies
Expand Down
29 changes: 23 additions & 6 deletions lib/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import ContentstackCollection from './contentstackCollection'
* await publishFn.call(entryInstance, { publishDetails: {...}, locale: 'en-us' })
*/
export const publish = (http, type) => {
return async function ({ publishDetails, locale = null, version = null, scheduledAt = null }) {
return async function ({ publishDetails, locale = null, version = null, scheduledAt = null, headers: extraHeaders = {}, params = {} }) {
const url = this.urlPath + '/publish'
const headers = {
headers: {
Expand All @@ -22,7 +22,10 @@ export const publish = (http, type) => {
} || {}
const httpBody = {}
httpBody[type] = cloneDeep(publishDetails)
return publishUnpublish(http, url, httpBody, headers, locale, version, scheduledAt)
return publishUnpublish(http, url, httpBody, headers, locale, version, scheduledAt, {
headers: extraHeaders,
params
})
}
}

Expand All @@ -36,7 +39,7 @@ export const publish = (http, type) => {
* await unpublishFn.call(entryInstance, { publishDetails: {...}, locale: 'en-us' })
*/
export const unpublish = (http, type) => {
return async function ({ publishDetails, locale = null, version = null, scheduledAt = null }) {
return async function ({ publishDetails, locale = null, version = null, scheduledAt = null, headers: extraHeaders = {}, params = {} }) {
const url = this.urlPath + '/unpublish'
const headers = {
headers: {
Expand All @@ -45,7 +48,10 @@ export const unpublish = (http, type) => {
} || {}
const httpBody = {}
httpBody[type] = cloneDeep(publishDetails)
return publishUnpublish(http, url, httpBody, headers, locale, version, scheduledAt)
return publishUnpublish(http, url, httpBody, headers, locale, version, scheduledAt, {
headers: extraHeaders,
params
})
}
}

Expand All @@ -58,11 +64,12 @@ export const unpublish = (http, type) => {
* @param {string|null} locale - Locale code.
* @param {number|null} version - Version number.
* @param {string|null} scheduledAt - Scheduled date/time in ISO format.
* @param {Object} [configExtras={}] - Optional `{ headers, params }` merged into the axios request (stack headers stay in `headers.headers`).
* @returns {Promise<Object>} Promise that resolves to response data.
* @async
* @private
*/
export const publishUnpublish = async (http, url, httpBody, headers, locale = null, version = null, scheduledAt = null) => {
export const publishUnpublish = async (http, url, httpBody, headers, locale = null, version = null, scheduledAt = null, configExtras = {}) => {
if (locale !== null) {
httpBody.locale = locale
}
Expand All @@ -72,8 +79,18 @@ export const publishUnpublish = async (http, url, httpBody, headers, locale = nu
if (scheduledAt !== null) {
httpBody.scheduled_at = scheduledAt
}
const requestConfig = {
headers: {
...cloneDeep(headers?.headers || {}),
...cloneDeep(configExtras?.headers || {})
},
params: {
...cloneDeep(headers?.params || {}),
...cloneDeep(configExtras?.params || {})
}
}
try {
const response = await http.post(url, httpBody, headers)
const response = await http.post(url, httpBody, requestConfig)
if (response.data) {
const data = response.data || {}
if (http?.httpClientParams?.headers?.api_version) {
Expand Down
52 changes: 45 additions & 7 deletions lib/stack/contentType/entry/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,22 +285,60 @@ export function Entry (http, data) {
* @description The variants call returns a Variants instance for managing variants of an entry.
* @memberof Entry
* @func variants
* @param {String=} uid - Variant UID. If not provided, returns Variants instance for querying all variants.
* @param {string|string[]=} variantUidOrUids - Variant UID, list of UIDs (comma-separated in the path), or omit to query all variants.
* @param {string=} branchName - Optional branch UID or alias for this variants scope (sent as the branch header). Omit to use the stack default branch.
* @returns {Variants} Instance of Variants.
* @example
* import * as contentstack from '@contentstack/management'
* const client = contentstack.client()
* const variants = client.stack({ api_key: 'api_key'}).contentType('content_type_uid').entry('uid').variants('uid')
* variants.fetch()
* .then((response) => console.log(response));
* @example
* client.stack({ api_key: 'api_key'}).contentType('content_type_uid').entry('uid').variants('uid', 'branch_name').update(data)
* .then((response) => console.log(response));
*/
this.variants = (uid = null) => {
const data = { stackHeaders: this.stackHeaders }
data.content_type_uid = this.content_type_uid
data.entry_uid = this.uid
if (uid) {
data.variants_uid = uid
this.variants = (variantUidOrUids, branchName) => {
const uidInput = variantUidOrUids === undefined ? null : variantUidOrUids
const branch =
typeof branchName === 'string' && branchName !== ''
? branchName
: undefined

const data = {
content_type_uid: this.content_type_uid,
entry_uid: this.uid
}

if (branch === undefined) {
data.stackHeaders = this.stackHeaders
} else {
data.stackHeaders = {
...cloneDeep(this.stackHeaders || {}),
branch
}
}

let variantsUid = null
if (Array.isArray(uidInput)) {
const uids = uidInput.filter(
(uid) => typeof uid === 'string' && uid.length > 0
)
if (uids.length === 1) {
variantsUid = uids[0]
} else if (uids.length > 1) {
variantsUid = uids.join(',')
}
} else if (typeof uidInput === 'string' && uidInput.length > 0) {
variantsUid = uidInput
} else if (uidInput != null && uidInput !== '') {
variantsUid = uidInput
}

if (variantsUid != null && variantsUid !== '') {
data.variants_uid = variantsUid
}

return new Variants(http, data)
}

Expand Down
102 changes: 98 additions & 4 deletions lib/stack/contentType/entry/variants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import cloneDeep from 'lodash/cloneDeep'
import {
deleteEntity,
fetch,
query
query,
publishUnpublish
}
from '../../../../entity'
import error from '../../../../core/contentstackError'
Expand All @@ -14,8 +15,19 @@ import { bindModuleHeaders } from '../../../../core/moduleHeaderSupport.js'
export function Variants (http, data) {
Object.assign(this, cloneDeep(data))
this.urlPath = `/content_types/${this.content_type_uid}/entries/${this.entry_uid}/variants`
if (data && data.variants_uid) {
this.urlPath += `/${this.variants_uid}`
let variantPathSegment = ''
if (data?.variants_uid != null && data.variants_uid !== '') {
if (Array.isArray(data.variants_uid)) {
variantPathSegment = data.variants_uid
.filter((uid) => typeof uid === 'string' && uid.length > 0)
.join(',')
} else {
variantPathSegment = String(data.variants_uid)
}
}
if (variantPathSegment) {
this.urlPath += `/${variantPathSegment}`
const entryBaseUrlPath = `/content_types/${this.content_type_uid}/entries/${this.entry_uid}`
/**
* @description The Update a variant call updates an existing variant for the selected content type.
* @memberof Variants
Expand All @@ -38,7 +50,7 @@ export function Variants (http, data) {
* }
* }
* }
* client.stack({ api_key: 'api_key'}).contentType('content_type_uid').entry('entry_uid').variants('uid').update(data)
* client.stack({ api_key: 'api_key'}).contentType('content_type_uid').entry('entry_uid').variants('uid', 'branch_name').update(data)
* .then((variants) => console.log(variants))
*/
this.update = async (data, params = {}) => {
Expand Down Expand Up @@ -121,6 +133,88 @@ export function Variants (http, data) {
return error(err)
}
}

/**
* @description Publishes via the entry publish endpoint (POST .../entries/{entry_uid}/publish). Pass `publishDetails` as the object nested under `entry` (environments, locales, variants, variant_rules, etc.). Optional `headers` and `params` are merged into the HTTP request.
* @memberof Variants
* @func publish
* @param {Object} options
* @param {Object} options.publishDetails - Payload for the `entry` property (e.g. environments, locales, variants, variant_rules).
* @param {String|null} [options.locale] - Top-level `locale` on the request body.
* @param {Number|null} [options.version] - Top-level `version` on the request body.
* @param {String|null} [options.scheduledAt] - Top-level `scheduled_at` (ISO) on the request body.
* @param {Object} [options.headers={}] - Extra request headers merged with stack headers.
* @param {Object} [options.params={}] - Query string parameters for the request.
* @returns {Promise<Object>} Response data (e.g. notice, job_id).
* @example
* import * as contentstack from '@contentstack/management'
* const client = contentstack.client()
* await client.stack({ api_key: 'api_key' }).contentType('ct').entry('entry_uid').variants('variant_uid').publish({
* publishDetails: {
* environments: ['production'],
* locales: ['en-us'],
* variants: [{ uid: 'variant_uid', version: 1 }],
* variant_rules: { publish_latest_base: false, publish_latest_base_conditionally: true }
* },
* locale: 'en-us'
* })
*/
this.publish = async ({
publishDetails,
locale = null,
version = null,
scheduledAt = null,
headers: extraHeaders = {},
params = {}
}) => {
const url = `${entryBaseUrlPath}/publish`
const httpBody = {}
httpBody.entry = cloneDeep(publishDetails)
const baseHeaders = {
headers: {
...cloneDeep(this.stackHeaders)
}
}
return publishUnpublish(http, url, httpBody, baseHeaders, locale, version, scheduledAt, {
headers: extraHeaders,
params
})
}

/**
* @description Unpublishes via the entry unpublish endpoint (POST .../entries/{entry_uid}/unpublish). Pass `publishDetails` as the object nested under `entry`. Optional `headers` and `params` are merged into the HTTP request.
* @memberof Variants
* @func unpublish
* @param {Object} options
* @param {Object} options.publishDetails - Payload for the `entry` property (e.g. environments, locales, variants).
* @param {String|null} [options.locale] - Top-level `locale` on the request body.
* @param {Number|null} [options.version] - Top-level `version` on the request body.
* @param {String|null} [options.scheduledAt] - Top-level `scheduled_at` (ISO) on the request body.
* @param {Object} [options.headers={}] - Extra request headers merged with stack headers.
* @param {Object} [options.params={}] - Query string parameters for the request.
* @returns {Promise<Object>} Response data (e.g. notice, job_id).
*/
this.unpublish = async ({
publishDetails,
locale = null,
version = null,
scheduledAt = null,
headers: extraHeaders = {},
params = {}
}) => {
const url = `${entryBaseUrlPath}/unpublish`
const httpBody = {}
httpBody.entry = cloneDeep(publishDetails)
const baseHeaders = {
headers: {
...cloneDeep(this.stackHeaders)
}
}
return publishUnpublish(http, url, httpBody, baseHeaders, locale, version, scheduledAt, {
headers: extraHeaders,
params
})
}
} else {
/**
* @description The Query on Variants will allow you to fetch details of all or specific Variants.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/management",
"version": "1.30.4",
"version": "1.31.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",
Expand Down
Loading
Loading