Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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: 2 additions & 2 deletions .github/workflows/check-version-bump.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ jobs:
echo "::error::Version bump required: package.json version ($PKG_VERSION) equals latest tag ($LATEST_TAG). Please bump the version in package.json."
exit 1
fi
CHANGELOG_VERSION=$(sed -nE 's/^## \[v?([0-9]+\.[0-9]+\.[0-9]+).*/\1/p' CHANGELOG.md | head -1)
CHANGELOG_VERSION=$(sed -nE 's/^(## \[v?|### Version: )([0-9]+\.[0-9]+\.[0-9]+).*/\2/p' CHANGELOG.md | head -1)
if [ -z "$CHANGELOG_VERSION" ]; then
echo "::error::Could not find a version entry in CHANGELOG.md (expected line like '## [v1.0.0](...)')."
echo "::error::Could not find a version entry in CHANGELOG.md (expected '## [v1.0.0](...)' or '### Version: 1.0.0')."
exit 1
fi
if [ "$CHANGELOG_VERSION" != "$PKG_VERSION" ]; then
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### Version: 5.3.0
#### Date: May-18-2026
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.

### Version: 5.2.0
#### Date: Apr-09-2026
Enhancement: `ContentTypeQuery` extends `BaseQuery` so `stack.contentType()` supports `paginate`, `skip`, `limit`, and related query helpers without mutating `_queryParams`; `includeGlobalFieldSchema()` and `find()` stay backward compatible. Expanded unit tests for `ContentTypeQuery`.
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/delivery-sdk",
"version": "5.2.0",
"version": "5.3.0",
"type": "module",
"license": "MIT",
"engines": {
Expand Down
21 changes: 21 additions & 0 deletions src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,24 @@ export function encodeQueryParams(params: params): params {

return encodedParams;
}

/**
* Builds request headers for entry variant requests.
* @param variants - Comma-separated variant UID(s)
* @param branch - Optional branch name to scope the variant request
*/
export function buildVariantRequestHeaders(
variants: string,
branch?: string
): Record<string, string> | undefined {
const headers: Record<string, string> = {};

if (variants) {
headers['x-cs-variant-uid'] = variants;
}
if (branch) {
headers.branch = branch;
}

return Object.keys(headers).length > 0 ? headers : undefined;
}
24 changes: 17 additions & 7 deletions src/entries/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AxiosInstance, getData } from '@contentstack/core';
import { Query } from '../query';
import { BaseQuery } from '../query';
import { FindResponse } from '../common/types';
import { encodeQueryParams } from '../common/utils';
import { buildVariantRequestHeaders, encodeQueryParams } from '../common/utils';
import { ErrorMessages } from '../common/error-messages';

export class Entries extends BaseQuery {
Expand All @@ -14,6 +14,7 @@ export class Entries extends BaseQuery {
this._contentTypeUid = contentTypeUid;
this._urlPath = `/content_types/${this._contentTypeUid}/entries`;
this._variants = '';
this._variantsBranch = '';
}

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

return new Query(this._client, this._parameters, this._queryParams, this._variants, this._contentTypeUid);
return new Query(this._client, this._parameters, this._queryParams, this._variants, this._contentTypeUid, this._variantsBranch);
}

/**
* @method variants
* @memberof Entries
* @description The variant header will be added to axios client
* @description The variant header will be added to axios client. Branch is optional.
* @param {string | string[]} variants - Variant UID or UIDs
* @param {string} [branchName] - Optional branch name sent as the `branch` header
* @returns {Entries}
* @example
* import contentstack from '@contentstack/delivery-sdk'
*
* const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
* const result = await stack.contentType('abc').entry().variants('xyz').find();
* const resultWithBranch = await stack.contentType('abc').entry().variants('xyz', 'branch_name').find();
*/
variants(variants: string | string[]): Entries {
variants(variants: string | string[], branchName?: string): Entries {
if (Array.isArray(variants) && variants.length > 0) {
this._variants = variants.join(',');
} else if (typeof variants == 'string' && variants.length > 0) {
this._variants = variants;
}
if (typeof branchName === 'string' && branchName.length > 0) {
this._variantsBranch = branchName;
}
return this;
}

Expand Down Expand Up @@ -320,10 +329,11 @@ export class Entries extends BaseQuery {
contentTypeUid: this._contentTypeUid
};

if (this._variants) {
const variantHeaders = buildVariantRequestHeaders(this._variants, this._variantsBranch);
if (variantHeaders) {
getRequestOptions.headers = {
...getRequestOptions.headers,
'x-cs-variant-uid': this._variants
...variantHeaders
};
}
const response = await getData(this._client, this._urlPath, getRequestOptions);
Expand Down
18 changes: 14 additions & 4 deletions src/entries/entry.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AxiosInstance, getData } from '@contentstack/core';
import { ErrorMessages } from '../common/error-messages';
import { buildVariantRequestHeaders } from '../common/utils';

interface EntryResponse<T> {
entry: T;
Expand All @@ -10,13 +11,15 @@ export class Entry {
private _entryUid: string;
private _urlPath: string;
protected _variants: string;
protected _variantsBranch: string;
_queryParams: { [key: string]: string | number | string[] } = {};
constructor(client: AxiosInstance, contentTypeUid: string, entryUid: string) {
this._client = client;
this._contentTypeUid = contentTypeUid;
this._entryUid = entryUid;
this._urlPath = `/content_types/${this._contentTypeUid}/entries/${this._entryUid}`;
this._variants = '';
this._variantsBranch = '';
}

/**
Expand All @@ -39,20 +42,26 @@ export class Entry {
/**
* @method variants
* @memberof Entry
* @description The variant header will be added to axios client
* @description The variant header will be added to axios client. Branch is optional.
* @param {string | string[]} variants - Variant UID or UIDs
* @param {string} [branchName] - Optional branch name sent as the `branch` header
* @returns {Entry}
* @example
* import contentstack from '@contentstack/delivery-sdk'
*
* const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
* const result = await stack.contentType('abc').entry('entry_uid').variants('xyz').fetch();
* const resultWithBranch = await stack.contentType('abc').entry('entry_uid').variants('xyz', 'branch_name').fetch();
*/
variants(variants: string | string[]): this {
variants(variants: string | string[], branchName?: string): this {
if (Array.isArray(variants) && variants.length > 0) {
this._variants = variants.join(',');
} else if (typeof variants == 'string' && variants.length > 0) {
this._variants = variants;
}
if (typeof branchName === 'string' && branchName.length > 0) {
this._variantsBranch = branchName;
}

return this;
}
Expand Down Expand Up @@ -189,10 +198,11 @@ export class Entry {
contentTypeUid: this._contentTypeUid,
entryUid: this._entryUid
};
if (this._variants) {
const variantHeaders = buildVariantRequestHeaders(this._variants, this._variantsBranch);
if (variantHeaders) {
getRequestOptions.headers = {
...getRequestOptions.headers,
'x-cs-variant-uid': this._variants
...variantHeaders
};
}

Expand Down
8 changes: 5 additions & 3 deletions src/query/base-query.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AxiosInstance, getData } from '@contentstack/core';
import { Pagination } from '../common/pagination';
import { FindResponse, params } from '../common/types';
import { encodeQueryParams } from '../common/utils';
import { buildVariantRequestHeaders, encodeQueryParams } from '../common/utils';
import type { Query } from './query';

export class BaseQuery extends Pagination {
Expand All @@ -10,6 +10,7 @@ export class BaseQuery extends Pagination {
protected _client!: AxiosInstance;
protected _urlPath!: string;
protected _variants!: string;
protected _variantsBranch!: string;

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

if (this._variants) {
const variantHeaders = buildVariantRequestHeaders(this._variants, this._variantsBranch);
if (variantHeaders) {
getRequestOptions.headers = {
...getRequestOptions.headers,
'x-cs-variant-uid': this._variants
...variantHeaders
};
}
const response = await getData(this._client, this._urlPath, getRequestOptions);
Expand Down
33 changes: 27 additions & 6 deletions src/query/query.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import { AxiosInstance, getData } from '@contentstack/core';
import { BaseQuery } from './base-query';
import { BaseQueryParameters, QueryOperation, QueryOperator, TaxonomyQueryOperation, params, queryParams, FindResponse } from '../common/types';
import { encodeQueryParams } from '../common/utils';
import { buildVariantRequestHeaders, encodeQueryParams } from '../common/utils';
import { ErrorMessages } from '../common/error-messages';

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

constructor(client: AxiosInstance, params: params, queryParams: queryParams, variants?: string, uid?: string, queryObj?: { [key: string]: any }) {
constructor(
client: AxiosInstance,
params: params,
queryParams: queryParams,
variants?: string,
uid?: string,
variantsBranchOrQueryObj?: string | { [key: string]: any },
queryObj?: { [key: string]: any }
) {
super();
this._client = client;
this._contentTypeUid = uid;
Expand All @@ -16,11 +24,23 @@ export class Query extends BaseQuery {
this._queryParams = queryParams || {};
this._variants = variants || '';

let variantsBranch = '';
let resolvedQueryObj: { [key: string]: any } | undefined;

if (typeof variantsBranchOrQueryObj === 'string') {
variantsBranch = variantsBranchOrQueryObj;
resolvedQueryObj = queryObj;
} else if (variantsBranchOrQueryObj) {
resolvedQueryObj = variantsBranchOrQueryObj;
}

this._variantsBranch = variantsBranch;

if (!uid) {
this._urlPath = `/assets`;
}
if (queryObj) {
this._parameters = { ...this._parameters, ...queryObj };
if (resolvedQueryObj) {
this._parameters = { ...this._parameters, ...resolvedQueryObj };
}
}
// Validate if input is alphanumeric
Expand Down Expand Up @@ -651,10 +671,11 @@ export class Query extends BaseQuery {
contentTypeUid: this._contentTypeUid
};

if (this._variants) {
const variantHeaders = buildVariantRequestHeaders(this._variants, this._variantsBranch);
if (variantHeaders) {
getRequestOptions.headers = {
...getRequestOptions.headers,
'x-cs-variant-uid': this._variants
...variantHeaders
};
}
const response = await getData(this._client, this._urlPath, getRequestOptions);
Expand Down
Loading
Loading