Skip to content
620 changes: 314 additions & 306 deletions package-lock.json

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions src/lib/asset-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,28 @@ export class AssetQuery extends BaseQuery {

return this;
}

/**
* @method assetFields
* @memberof AssetQuery
* @description Include specific asset fields in the response (CDA getAssets).
* Use with asset_fields[]: user_defined_fields, embedded, ai_suggested, visual_markups.
* @example
* import contentstack from '@contentstack/delivery-sdk'
*
* const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
* const result = await stack.asset().assetFields("user_defined_fields", "embedded").find();
*
* @param {...string} fields - Asset field names to include (e.g. user_defined_fields, embedded, ai_suggested, visual_markups)
* @returns {AssetQuery} - Returns the AssetQuery instance for chaining.
*/
assetFields(...fields: string[]): this {
if (fields.length > 0) {
this._queryParams['asset_fields[]'] = fields;
}
return this;
}

/**
* @method query
* @memberof AssetQuery
Expand Down
23 changes: 22 additions & 1 deletion src/lib/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { AxiosInstance, getData } from '@contentstack/core';
export class Asset {
private _client: AxiosInstance;
private _urlPath: string;
_queryParams: { [key: string]: string | number } = {};
_queryParams: { [key: string]: string | number | string[] } = {};

constructor(client: AxiosInstance, assetUid: string) {
this._client = client;
Expand Down Expand Up @@ -129,6 +129,27 @@ export class Asset {
return this;
}

/**
* @method assetFields
* @memberof Asset
* @description Include specific asset fields in the response (CDA getAssets - single asset).
* Use with asset_fields[]: user_defined_fields, embedded, ai_suggested, visual_markups.
* @example
* import contentstack from '@contentstack/delivery-sdk'
*
* const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
* const result = await stack.asset("assetUid").assetFields("user_defined_fields", "embedded").fetch();
*
* @param {...string} fields - Asset field names to include (e.g. user_defined_fields, embedded, ai_suggested, visual_markups)
* @returns {Asset} - Returns the Asset instance for chaining.
*/
assetFields(...fields: string[]): this {
if (fields.length > 0) {
this._queryParams['asset_fields[]'] = fields;
}
return this;
}

/**
* @method fetch
* @memberof Asset
Expand Down
21 changes: 21 additions & 0 deletions src/lib/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,27 @@ export class Entries extends BaseQuery {
return this;
}

/**
* @method assetFields
* @memberof Entries
* @description Include specific asset fields in the response (CDA getEntry/entries).
* Use with asset_fields[]: user_defined_fields, embedded, ai_suggested, visual_markups.
* @example
* import contentstack from '@contentstack/delivery-sdk'
*
* const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
* const result = await stack.contentType("contentTypeUid").entry().assetFields("user_defined_fields", "embedded").find();
*
* @param {...string} fields - Asset field names to include (e.g. user_defined_fields, embedded, ai_suggested, visual_markups)
* @returns {Entries} - Returns the Entries instance for chaining.
*/
assetFields(...fields: string[]): this {
if (fields.length > 0) {
this._queryParams['asset_fields[]'] = fields;
}
return this;
}

/**
* Override find method to include content type UID directly for better caching
*/
Expand Down
21 changes: 21 additions & 0 deletions src/lib/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,4 +273,25 @@ export class Entry {
}
return this;
}

/**
* @method assetFields
* @memberof Entry
* @description Include specific asset fields in the response (CDA getEntry).
* Use with asset_fields[]: user_defined_fields, embedded, ai_suggested, visual_markups.
* @example
* import contentstack from '@contentstack/delivery-sdk'
*
* const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
* const result = await stack.contentType("contentTypeUid").entry("entryUid").assetFields("user_defined_fields", "embedded").fetch();
*
* @param {...string} fields - Asset field names to include (e.g. user_defined_fields, embedded, ai_suggested, visual_markups)
* @returns {Entry} - Returns the Entry instance for chaining.
*/
assetFields(...fields: string[]): this {
if (fields.length > 0) {
this._queryParams['asset_fields[]'] = fields;
}
return this;
}
}
14 changes: 14 additions & 0 deletions test/api/asset-query.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,20 @@ describe("AssetQuery API tests", () => {
console.log('No assets found in stack - test data dependent');
}
});
it("should query assets with asset_fields[] CDA param (user_defined_fields, embedded, ai_suggested, visual_markups)", async () => {
const result = await makeAssetQuery()
.assetFields("user_defined_fields", "embedded", "ai_suggested", "visual_markups")
.limit(2)
.find<TAsset>();
if (result.assets) {
expect(result.assets).toBeDefined();
if (result.assets.length > 0) {
expect(result.assets[0].uid).toBeDefined();
expect(result.assets[0]._version).toBeDefined();
expect(result.assets[0].content_type).toBeDefined();
}
}
});
});
function makeAssetQuery(): AssetQuery {
const asset = stack.asset();
Expand Down
11 changes: 11 additions & 0 deletions test/api/asset.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,17 @@ describe('Asset API tests', () => {
expect(result.created_by).toBeDefined();
expect(result.updated_by).toBeDefined();
});

it('should fetch asset with asset_fields[] CDA param (user_defined_fields, embedded, ai_suggested, visual_markups)', async () => {
const result = await makeAsset(assetUid)
.assetFields('user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups')
.fetch<BaseAsset>();
expect(result).toBeDefined();
expect(result.uid).toBeDefined();
expect(result._version).toBeDefined();
expect(result.url).toBeDefined();
expect(result.filename).toBeDefined();
});
});
function makeAsset(uid = ''): Asset {
const asset = stack.asset(uid);
Expand Down
13 changes: 13 additions & 0 deletions test/api/entries.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,19 @@ describe("Entries API test cases", () => {
if (data.entries) expect(data.entries.length).toBeGreaterThan(0);
});

it("should query entries with asset_fields[] CDA param (user_defined_fields, embedded, ai_suggested, visual_markups)", async () => {
const result = await makeEntries(BLOG_POST_CT)
.assetFields("user_defined_fields", "embedded", "ai_suggested", "visual_markups")
.find<TEntry>();
if (result.entries) {
expect(result.entries).toBeDefined();
if (result.entries.length > 0) {
expect(result.entries[0].uid).toBeDefined();
expect(result.entries[0]._version).toBeDefined();
}
}
});

it("CT Taxonomies Query: Get Entries With Taxonomy Terms Parent and Excluding the term itself ($above, level)", async () => {
// ABOVE operation finds entries tagged with PARENT terms of the given term
// Requires a child term (e.g., term_one_child) to find its parents
Expand Down
10 changes: 10 additions & 0 deletions test/api/entry.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ describe('Entry API tests', () => {
expect(result._version).toBeDefined();
expect(result.publish_details).toBeDefined();
});

it('should fetch entry with asset_fields[] CDA param (user_defined_fields, embedded, ai_suggested, visual_markups)', async () => {
const result = await makeEntry(entryUid)
.assetFields('user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups')
.fetch<TEntry>();
expect(result).toBeDefined();
expect(result.uid).toBeDefined();
expect(result._version).toBeDefined();
expect(result.locale).toBeDefined();
});
});
function makeEntry(uid = ''): Entry {
const entry = stack.contentType(BLOG_POST_CT).entry(uid);
Expand Down
22 changes: 22 additions & 0 deletions test/unit/asset-query.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ describe('AssetQuery class', () => {
expect(assetQuery._queryParams.locale).toBe('en-us');
});

it('should add "asset_fields[]" in _queryParams when assetFields method is called', () => {
const returnedValue = assetQuery.assetFields('user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups');
expect(returnedValue).toBeInstanceOf(AssetQuery);
expect(assetQuery._queryParams['asset_fields[]']).toEqual(['user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups']);
});

it('should not set asset_fields[] when assetFields is called with no arguments', () => {
assetQuery.assetFields();
expect(assetQuery._queryParams['asset_fields[]']).toBeUndefined();
});

it('should return response data when successful', async () => {
mockClient.onGet('/assets').reply(200, assetQueryFindResponseDataMock);
const response = await assetQuery.find();
Expand All @@ -72,4 +83,15 @@ describe('AssetQuery class', () => {
expect(returnedValue._parameters).toEqual({ fieldUid: 'value' });
}
});

it('should call find with asset_fields[] query params when assetFields is set', async () => {
mockClient.onGet('/assets').reply((config) => {
expect(config.params).toBeDefined();
expect(config.params['asset_fields[]']).toEqual(['user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups']);
return [200, assetQueryFindResponseDataMock];
});
assetQuery.assetFields('user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups');
const result = await assetQuery.find();
expect(result).toEqual(assetQueryFindResponseDataMock);
});
});
22 changes: 22 additions & 0 deletions test/unit/asset.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ describe('Asset class', () => {
expect(asset._queryParams.version).toBe('1');
});

it('should add "asset_fields[]" in _queryParams when assetFields method is called', () => {
const returnedValue = asset.assetFields('user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups');
expect(returnedValue).toBeInstanceOf(Asset);
expect(asset._queryParams['asset_fields[]']).toEqual(['user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups']);
});

it('should not set asset_fields[] when assetFields is called with no arguments', () => {
asset.assetFields();
expect(asset._queryParams['asset_fields[]']).toBeUndefined();
});

it('should add "fetch" in _queryParams when fetch method is called', async () => {
mockClient.onGet(`/assets/assetUid`).reply(200, assetFetchDataMock);
const returnedValue = await asset.fetch();
Expand All @@ -74,4 +85,15 @@ describe('Asset class', () => {

expect(result).toEqual(responseWithoutAsset);
});

it('should call fetch with asset_fields[] query params when assetFields is set', async () => {
mockClient.onGet(`/assets/assetUid`).reply((config) => {
expect(config.params).toBeDefined();
expect(config.params['asset_fields[]']).toEqual(['user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups']);
return [200, assetFetchDataMock];
});
asset.assetFields('user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups');
const result = await asset.fetch();
expect(result).toEqual(assetFetchDataMock.asset);
});
});
22 changes: 22 additions & 0 deletions test/unit/entries.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ describe('Entries class', () => {
expect(entry._queryParams.include_content_type).toBe('true');
});

it('should add "asset_fields[]" in _queryParams when assetFields method is called', () => {
const returnedValue = entry.assetFields('user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups');
expect(returnedValue).toBeInstanceOf(Entries);
expect(entry._queryParams['asset_fields[]']).toEqual(['user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups']);
});

it('should not set asset_fields[] when assetFields is called with no arguments', () => {
entry.assetFields();
expect(entry._queryParams['asset_fields[]']).toBeUndefined();
});

it('should add "include_reference_content_type_uid" in _queryParams when includeReferenceContentTypeUID method is called', () => {
const returnedValue = entry.includeReferenceContentTypeUID();
expect(returnedValue).toBeInstanceOf(Entries);
Expand Down Expand Up @@ -149,6 +160,17 @@ describe('Entries class', () => {
expect(returnedValue).toEqual(entryFindMock);
});

it('should call find with asset_fields[] query params when assetFields is set', async () => {
mockClient.onGet(`/content_types/contentTypeUid/entries`).reply((config) => {
expect(config.params).toBeDefined();
expect(config.params['asset_fields[]']).toEqual(['user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups']);
return [200, entryFindMock];
});
entry.assetFields('user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups');
const result = await entry.find();
expect(result).toEqual(entryFindMock);
});

it('CT Taxonomy Query: Get entries with one term', () => {
const query = entry.query().where("taxonomies.taxonomy_uid", QueryOperation.EQUALS, "term_uid");
expect(query._parameters).toEqual({"taxonomies.taxonomy_uid": "term_uid"});
Expand Down
24 changes: 24 additions & 0 deletions test/unit/entry.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,17 @@ describe('Entry class', () => {
expect(entry._queryParams.key3).toEqual(['value3']);
});

it('should add "asset_fields[]" in _queryParams when assetFields method is called', () => {
const returnedValue = entry.assetFields('user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups');
expect(returnedValue).toBeInstanceOf(Entry);
expect(entry._queryParams['asset_fields[]']).toEqual(['user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups']);
});

it('should not set asset_fields[] when assetFields is called with no arguments', () => {
entry.assetFields();
expect(entry._queryParams['asset_fields[]']).toBeUndefined();
});

it('should get the API response when fetch method is called', async () => {
mockClient.onGet(`/content_types/contentTypeUid/entries/entryUid`).reply(200, entryFetchMock);
const returnedValue = await entry.fetch();
Expand Down Expand Up @@ -237,4 +248,17 @@ describe('Fetch with variants', () => {

expect(result).toEqual(responseWithoutEntry);
});

it('should call fetch with asset_fields[] query params when assetFields is set', async () => {
mockClient.onGet('/content_types/contentTypeUid/entries/entryUid').reply((config) => {
expect(config.params).toBeDefined();
expect(config.params['asset_fields[]']).toEqual(['user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups']);
return [200, entryFetchMock];
});

entry.assetFields('user_defined_fields', 'embedded', 'ai_suggested', 'visual_markups');
const result = await entry.fetch();

expect(result).toEqual(entryFetchMock.entry);
});
})