Skip to content

Commit fb74608

Browse files
authored
Merge pull request #361 from contentstack/enh/DX-7322
Added Branches support in entry variants
2 parents 39ee605 + 40256be commit fb74608

13 files changed

Lines changed: 467 additions & 24 deletions

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
### Version: 5.5.0
2+
#### Date: Jul-27-2026
3+
Enhancement: Entry variants support an optional branch name as the second argument to `variants()` on `Entry` and `Entries`. When provided, the branch is sent as the `branch` request header together with `x-cs-variant-uid`. Existing `variants(uid)` and `variants(uids)` calls remain backward compatible. Added unit and API tests for variant + branch requests.
4+
15
### Version: 5.4.0
26
#### Date: Jul-16-2026
37
Enhancement: Removed `locale?` parameter from `Taxonomy.fetch()` and `Term.fetch()` — locale is now set via the chainable `.param('locale', value)` API, consistent with other query modifiers.

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/delivery-sdk",
3-
"version": "5.4.0",
3+
"version": "5.5.0",
44
"type": "module",
55
"license": "MIT",
66
"engines": {

src/common/utils.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,24 @@ export function encodeQueryParams(params: params): params {
3737

3838
return encodedParams;
3939
}
40+
41+
/**
42+
* Builds request headers for entry variant requests.
43+
* @param variants - Comma-separated variant UID(s)
44+
* @param branch - Optional branch name to scope the variant request
45+
*/
46+
export function buildVariantRequestHeaders(
47+
variants: string,
48+
branch?: string
49+
): Record<string, string> | undefined {
50+
const headers: Record<string, string> = {};
51+
52+
if (variants) {
53+
headers['x-cs-variant-uid'] = variants;
54+
}
55+
if (branch) {
56+
headers.branch = branch;
57+
}
58+
59+
return Object.keys(headers).length > 0 ? headers : undefined;
60+
}

src/entries/entries.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { AxiosInstance, getData } from '@contentstack/core';
22
import { Query } from '../query';
33
import { BaseQuery } from '../query';
44
import { FindResponse } from '../common/types';
5-
import { encodeQueryParams } from '../common/utils';
5+
import { buildVariantRequestHeaders, encodeQueryParams } from '../common/utils';
66
import { ErrorMessages } from '../common/error-messages';
77

88
export class Entries extends BaseQuery {
@@ -14,6 +14,7 @@ export class Entries extends BaseQuery {
1414
this._contentTypeUid = contentTypeUid;
1515
this._urlPath = `/content_types/${this._contentTypeUid}/entries`;
1616
this._variants = '';
17+
this._variantsBranch = '';
1718
}
1819

1920
/**
@@ -252,28 +253,36 @@ export class Entries extends BaseQuery {
252253
* const query = stack.contentType("contentTypeUid").entry().query();
253254
*/
254255
query(queryObj?: { [key: string]: any }) {
255-
if (queryObj) return new Query(this._client, this._parameters, this._queryParams, this._variants, this._contentTypeUid, queryObj);
256+
if (queryObj) {
257+
return new Query(this._client, this._parameters, this._queryParams, this._variants, this._contentTypeUid, this._variantsBranch, queryObj);
258+
}
256259

257-
return new Query(this._client, this._parameters, this._queryParams, this._variants, this._contentTypeUid);
260+
return new Query(this._client, this._parameters, this._queryParams, this._variants, this._contentTypeUid, this._variantsBranch);
258261
}
259262

260263
/**
261264
* @method variants
262265
* @memberof Entries
263-
* @description The variant header will be added to axios client
266+
* @description Stores the variant UID(s) and optional branch name, which are sent as the `x-cs-variant-uid` and `branch` headers on the request when find() is called.
267+
* @param {string | string[]} variants - Variant UID or UIDs
268+
* @param {string} [branchName] - Optional branch name sent as the `branch` header
264269
* @returns {Entries}
265270
* @example
266271
* import contentstack from '@contentstack/delivery-sdk'
267272
*
268273
* const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
269274
* const result = await stack.contentType('abc').entry().variants('xyz').find();
275+
* const resultWithBranch = await stack.contentType('abc').entry().variants('xyz', 'branch_name').find();
270276
*/
271-
variants(variants: string | string[]): Entries {
277+
variants(variants: string | string[], branchName?: string): Entries {
272278
if (Array.isArray(variants) && variants.length > 0) {
273279
this._variants = variants.join(',');
274280
} else if (typeof variants == 'string' && variants.length > 0) {
275281
this._variants = variants;
276282
}
283+
if (typeof branchName === 'string' && branchName.length > 0) {
284+
this._variantsBranch = branchName;
285+
}
277286
return this;
278287
}
279288

@@ -320,10 +329,11 @@ export class Entries extends BaseQuery {
320329
contentTypeUid: this._contentTypeUid
321330
};
322331

323-
if (this._variants) {
332+
const variantHeaders = buildVariantRequestHeaders(this._variants, this._variantsBranch);
333+
if (variantHeaders) {
324334
getRequestOptions.headers = {
325335
...getRequestOptions.headers,
326-
'x-cs-variant-uid': this._variants
336+
...variantHeaders
327337
};
328338
}
329339
const response = await getData(this._client, this._urlPath, getRequestOptions);

src/entries/entry.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { AxiosInstance, getData } from '@contentstack/core';
22
import { ErrorMessages } from '../common/error-messages';
3+
import { buildVariantRequestHeaders } from '../common/utils';
34

45
interface EntryResponse<T> {
56
entry: T;
@@ -10,13 +11,15 @@ export class Entry {
1011
private _entryUid: string;
1112
private _urlPath: string;
1213
protected _variants: string;
14+
protected _variantsBranch: string;
1315
_queryParams: { [key: string]: string | number | string[] } = {};
1416
constructor(client: AxiosInstance, contentTypeUid: string, entryUid: string) {
1517
this._client = client;
1618
this._contentTypeUid = contentTypeUid;
1719
this._entryUid = entryUid;
1820
this._urlPath = `/content_types/${this._contentTypeUid}/entries/${this._entryUid}`;
1921
this._variants = '';
22+
this._variantsBranch = '';
2023
}
2124

2225
/**
@@ -39,20 +42,26 @@ export class Entry {
3942
/**
4043
* @method variants
4144
* @memberof Entry
42-
* @description The variant header will be added to axios client
45+
* @description Stores the variant UID(s) and optional branch name, which are sent as the `x-cs-variant-uid` and `branch` headers on the request when fetch() is called.
46+
* @param {string | string[]} variants - Variant UID or UIDs
47+
* @param {string} [branchName] - Optional branch name sent as the `branch` header
4348
* @returns {Entry}
4449
* @example
4550
* import contentstack from '@contentstack/delivery-sdk'
4651
*
4752
* const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
4853
* const result = await stack.contentType('abc').entry('entry_uid').variants('xyz').fetch();
54+
* const resultWithBranch = await stack.contentType('abc').entry('entry_uid').variants('xyz', 'branch_name').fetch();
4955
*/
50-
variants(variants: string | string[]): this {
56+
variants(variants: string | string[], branchName?: string): this {
5157
if (Array.isArray(variants) && variants.length > 0) {
5258
this._variants = variants.join(',');
5359
} else if (typeof variants == 'string' && variants.length > 0) {
5460
this._variants = variants;
5561
}
62+
if (typeof branchName === 'string' && branchName.length > 0) {
63+
this._variantsBranch = branchName;
64+
}
5665

5766
return this;
5867
}
@@ -189,10 +198,11 @@ export class Entry {
189198
contentTypeUid: this._contentTypeUid,
190199
entryUid: this._entryUid
191200
};
192-
if (this._variants) {
201+
const variantHeaders = buildVariantRequestHeaders(this._variants, this._variantsBranch);
202+
if (variantHeaders) {
193203
getRequestOptions.headers = {
194204
...getRequestOptions.headers,
195-
'x-cs-variant-uid': this._variants
205+
...variantHeaders
196206
};
197207
}
198208

src/query/base-query.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { AxiosInstance, getData } from '@contentstack/core';
22
import { Pagination } from '../common/pagination';
33
import { FindResponse, params } from '../common/types';
4-
import { encodeQueryParams } from '../common/utils';
4+
import { buildVariantRequestHeaders, encodeQueryParams } from '../common/utils';
55
import type { Query } from './query';
66

77
export class BaseQuery extends Pagination {
@@ -10,6 +10,7 @@ export class BaseQuery extends Pagination {
1010
protected _client!: AxiosInstance;
1111
protected _urlPath!: string;
1212
protected _variants!: string;
13+
protected _variantsBranch!: string;
1314

1415
/**
1516
* Helper method to cast this instance to Query type
@@ -231,10 +232,11 @@ export class BaseQuery extends Pagination {
231232
contentTypeUid: this.extractContentTypeUidFromUrl()
232233
};
233234

234-
if (this._variants) {
235+
const variantHeaders = buildVariantRequestHeaders(this._variants, this._variantsBranch);
236+
if (variantHeaders) {
235237
getRequestOptions.headers = {
236238
...getRequestOptions.headers,
237-
'x-cs-variant-uid': this._variants
239+
...variantHeaders
238240
};
239241
}
240242
const response = await getData(this._client, this._urlPath, getRequestOptions);

src/query/query.ts

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,38 @@
11
import { AxiosInstance, getData } from '@contentstack/core';
22
import { BaseQuery } from './base-query';
33
import { BaseQueryParameters, QueryOperation, QueryOperator, TaxonomyQueryOperation, params, queryParams, FindResponse } from '../common/types';
4-
import { encodeQueryParams } from '../common/utils';
4+
import { buildVariantRequestHeaders, encodeQueryParams } from '../common/utils';
55
import { ErrorMessages } from '../common/error-messages';
66

77
export class Query extends BaseQuery {
88
private _contentTypeUid?: string;
99

10-
constructor(client: AxiosInstance, params: params, queryParams: queryParams, variants?: string, uid?: string, queryObj?: { [key: string]: any }) {
10+
constructor(
11+
client: AxiosInstance,
12+
params: params,
13+
queryParams: queryParams,
14+
variants?: string,
15+
uid?: string,
16+
queryObj?: { [key: string]: any }
17+
);
18+
constructor(
19+
client: AxiosInstance,
20+
params: params,
21+
queryParams: queryParams,
22+
variants?: string,
23+
uid?: string,
24+
variantsBranch?: string,
25+
queryObj?: { [key: string]: any }
26+
);
27+
constructor(
28+
client: AxiosInstance,
29+
params: params,
30+
queryParams: queryParams,
31+
variants?: string,
32+
uid?: string,
33+
variantsBranchOrQueryObj?: string | { [key: string]: any },
34+
queryObj?: { [key: string]: any }
35+
) {
1136
super();
1237
this._client = client;
1338
this._contentTypeUid = uid;
@@ -16,11 +41,23 @@ export class Query extends BaseQuery {
1641
this._queryParams = queryParams || {};
1742
this._variants = variants || '';
1843

44+
let variantsBranch = '';
45+
let resolvedQueryObj: { [key: string]: any } | undefined;
46+
47+
if (typeof variantsBranchOrQueryObj === 'string') {
48+
variantsBranch = variantsBranchOrQueryObj;
49+
resolvedQueryObj = queryObj;
50+
} else if (variantsBranchOrQueryObj) {
51+
resolvedQueryObj = variantsBranchOrQueryObj;
52+
}
53+
54+
this._variantsBranch = variantsBranch;
55+
1956
if (!uid) {
2057
this._urlPath = `/assets`;
2158
}
22-
if (queryObj) {
23-
this._parameters = { ...this._parameters, ...queryObj };
59+
if (resolvedQueryObj) {
60+
this._parameters = { ...this._parameters, ...resolvedQueryObj };
2461
}
2562
}
2663
// Validate if input is alphanumeric
@@ -651,10 +688,11 @@ export class Query extends BaseQuery {
651688
contentTypeUid: this._contentTypeUid
652689
};
653690

654-
if (this._variants) {
691+
const variantHeaders = buildVariantRequestHeaders(this._variants, this._variantsBranch);
692+
if (variantHeaders) {
655693
getRequestOptions.headers = {
656694
...getRequestOptions.headers,
657-
'x-cs-variant-uid': this._variants
695+
...variantHeaders
658696
};
659697
}
660698
const response = await getData(this._client, this._urlPath, getRequestOptions);

0 commit comments

Comments
 (0)