From 74d80644314a69df25ff1e5823b8b92444c4e3ad Mon Sep 17 00:00:00 2001 From: Nadeem Patwekar Date: Wed, 1 Oct 2025 12:29:29 +0530 Subject: [PATCH 1/4] fix: Add logging interceptors for request and response in stack function --- src/lib/contentstack.ts | 61 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/lib/contentstack.ts b/src/lib/contentstack.ts index 057a4e8c..c07f5a2b 100644 --- a/src/lib/contentstack.ts +++ b/src/lib/contentstack.ts @@ -104,6 +104,67 @@ export function stack(config: StackConfig): StackClass { }); }; } + // LogHandler interceptors + if (config.debug) { + // Request interceptor for logging + client.interceptors.request.use((requestConfig: any) => { + config.logHandler!('info', { + type: 'request', + method: requestConfig.method?.toUpperCase(), + url: requestConfig.url, + headers: requestConfig.headers, + params: requestConfig.params, + timestamp: new Date().toISOString() + }); + return requestConfig; + }); + + // Response interceptor for logging + client.interceptors.response.use( + (response: any) => { + const level = getLogLevelFromStatus(response.status); + config.logHandler!(level, { + type: 'response', + status: response.status, + statusText: response.statusText, + url: response.config?.url, + method: response.config?.method?.toUpperCase(), + headers: response.headers, + data: response.data, + timestamp: new Date().toISOString() + }); + return response; + }, + (error: any) => { + const status = error.response?.status || 0; + const level = getLogLevelFromStatus(status); + config.logHandler!(level, { + type: 'response_error', + status: status, + statusText: error.response?.statusText || error.message, + url: error.config?.url, + method: error.config?.method?.toUpperCase(), + error: error.message, + timestamp: new Date().toISOString() + }); + throw error; + } + ); + } + + // Helper function to determine log level based on HTTP status code + function getLogLevelFromStatus(status: number): string { + if (status >= 200 && status < 300) { + return 'info'; + } else if (status >= 300 && status < 500) { + return 'warn'; + } else if (status >= 500) { + return 'error'; + } else { + return 'debug'; + } + } + // Retry policy handlers const errorHandler = (error: any) => { return retryResponseErrorHandler(error, config, client); From 73a36137368b56692bffb284f14ad03a0ed76c8e Mon Sep 17 00:00:00 2001 From: Nadeem Patwekar Date: Wed, 1 Oct 2025 12:31:30 +0530 Subject: [PATCH 2/4] chore: Update version to 4.10.1 and add logHandler interceptors to CHANGELOG --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0700ef2..a750e691 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### Version: 4.10.1 +#### Date: Oct-01-2025 +Enhancement: Added logHandler interceptors for request and response logging + ### Version: 4.10.0 #### Date: Sep-22-2025 Fix: Enhance retry logic to use configured retryDelay diff --git a/package.json b/package.json index 3a5e1097..335a0b49 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/delivery-sdk", - "version": "4.10.0", + "version": "4.10.1", "type": "module", "license": "MIT", "main": "./dist/legacy/index.cjs", From f87365330fc428276c3f42b42a6ccc652a0391a0 Mon Sep 17 00:00:00 2001 From: Nadeem Patwekar Date: Wed, 1 Oct 2025 12:37:00 +0530 Subject: [PATCH 3/4] fix: Update log level determination for HTTP status codes in getLogLevelFromStatus function --- src/lib/contentstack.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/contentstack.ts b/src/lib/contentstack.ts index c07f5a2b..5a8d15f5 100644 --- a/src/lib/contentstack.ts +++ b/src/lib/contentstack.ts @@ -156,9 +156,9 @@ export function stack(config: StackConfig): StackClass { function getLogLevelFromStatus(status: number): string { if (status >= 200 && status < 300) { return 'info'; - } else if (status >= 300 && status < 500) { + } else if (status >= 300 && status < 400) { return 'warn'; - } else if (status >= 500) { + } else if (status >= 400) { return 'error'; } else { return 'debug'; From 240d7f5472ce1c843128ba028a9fe3f70efc8dfd Mon Sep 17 00:00:00 2001 From: "harshitha.d" Date: Mon, 13 Oct 2025 14:00:32 +0530 Subject: [PATCH 4/4] feat: Implement Taxonomy class and update TaxonomyQuery for enhanced taxonomy management --- src/index.ts | 1 + src/lib/stack.ts | 8 +++++++- src/lib/taxonomy-query.ts | 35 ++++++++++++++++++++++++++++------- src/lib/taxonomy.ts | 23 +++++++++++++++++++++++ test/api/taxonomy.spec.ts | 22 ++++++++++++++++++++++ test/api/types.ts | 17 +++++++++++++++++ test/unit/taxonomy.spec.ts | 26 ++++++++++++++++++++++++++ test/utils/mocks.ts | 26 +++++++++++++++++++++++++- 8 files changed, 149 insertions(+), 9 deletions(-) create mode 100644 src/lib/taxonomy.ts create mode 100644 test/api/taxonomy.spec.ts create mode 100644 test/unit/taxonomy.spec.ts diff --git a/src/index.ts b/src/index.ts index d108292b..704c9bde 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,5 +11,6 @@ export type { ImageTransform } from './lib/image-transform'; export type { AssetQuery } from './lib/asset-query'; export type { TaxonomyQuery } from './lib/taxonomy-query'; export type { ContentTypeQuery } from './lib/contenttype-query'; +export type { Taxonomy } from './lib/taxonomy'; export default contentstack; diff --git a/src/lib/stack.ts b/src/lib/stack.ts index 324533d5..815697d2 100644 --- a/src/lib/stack.ts +++ b/src/lib/stack.ts @@ -8,6 +8,7 @@ import { synchronization } from './synchronization'; import {TaxonomyQuery} from './taxonomy-query'; import { GlobalFieldQuery } from './global-field-query'; import { GlobalField } from './global-field'; +import { Taxonomy } from './taxonomy'; export class Stack { readonly config: StackConfig; @@ -27,6 +28,7 @@ export class Stack { * @returns {Asset} * @example * import contentstack from '@contentstack/delivery-sdk' +import { Taxonomy } from './taxonomy'; * * const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" }); * const asset = stack.asset() // For collection of asset @@ -77,7 +79,11 @@ export class Stack { * const taxonomy = stack.taxonomy() // For taxonomy query object */ - taxonomy(): TaxonomyQuery { + taxonomy(): TaxonomyQuery; + taxonomy(uid: string): Taxonomy; + taxonomy(uid?: string): Taxonomy | TaxonomyQuery { + if (uid) return new Taxonomy(this._client, uid); + return new TaxonomyQuery(this._client); } diff --git a/src/lib/taxonomy-query.ts b/src/lib/taxonomy-query.ts index 7ece8970..ec8ae9d6 100644 --- a/src/lib/taxonomy-query.ts +++ b/src/lib/taxonomy-query.ts @@ -1,10 +1,31 @@ import { Query } from "./query"; -import { AxiosInstance } from "@contentstack/core"; +import { AxiosInstance, getData } from "@contentstack/core"; +import { FindResponse } from "./types"; export class TaxonomyQuery extends Query { - constructor(client: AxiosInstance) { - super(client, {}, {}); // will need make changes to Query class so that CT uid is not mandatory - this._client = client; - this._urlPath = `/taxonomies/entries`; - } -}; \ No newline at end of file + constructor(client: AxiosInstance) { + super(client, {}, {}); // will need make changes to Query class so that CT uid is not mandatory + this._client = client; + this._urlPath = `/taxonomies/entries`; + } + /** + * @method find + * @memberof TaxonomyQuery + * @description Fetches all taxonomies of the stack using /taxonomy-manager endpoint + * @returns {Promise>} + * @example + * import contentstack from '@contentstack/delivery-sdk' + * + * const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" }); + * const taxonomyQuery = stack.taxonomy(); + * const result = await taxonomyQuery.find(); + */ + override async find(): Promise> { + this._urlPath = "/taxonomy-manager"; // TODO: change to /taxonomies + const response = await getData(this._client, this._urlPath, { + params: this._queryParams, + }); + + return response as FindResponse; + } +} \ No newline at end of file diff --git a/src/lib/taxonomy.ts b/src/lib/taxonomy.ts new file mode 100644 index 00000000..6e7a8745 --- /dev/null +++ b/src/lib/taxonomy.ts @@ -0,0 +1,23 @@ +import { AxiosInstance, getData } from '@contentstack/core'; + +export class Taxonomy { + private _client: AxiosInstance; + private _taxonomyUid: string; + private _urlPath: string; + + _queryParams: { [key: string]: string | number } = {}; + + constructor(client: AxiosInstance, taxonomyUid: string) { + this._client = client; + this._taxonomyUid = taxonomyUid; + this._urlPath = `/taxonomy-manager/${this._taxonomyUid}`; // TODO: change to /taxonomies/${this._taxonomyUid} + } + + async fetch(): Promise { + const response = await getData(this._client, this._urlPath); + + if (response.taxonomy) return response.taxonomy as T; + + return response; + } +} diff --git a/test/api/taxonomy.spec.ts b/test/api/taxonomy.spec.ts new file mode 100644 index 00000000..86850733 --- /dev/null +++ b/test/api/taxonomy.spec.ts @@ -0,0 +1,22 @@ +/* eslint-disable no-console */ +/* eslint-disable promise/always-return */ +import { stackInstance } from '../utils/stack-instance'; +import { TTaxonomies } from './types'; +import dotenv from 'dotenv'; +import { TaxonomyQuery } from '../../src/lib/taxonomy-query'; + +dotenv.config() + +const stack = stackInstance(); +describe('ContentType API test cases', () => { + it('should give taxonomies when taxonomies method is called', async () => { + const result = await makeTaxonomy().find(); + expect(result).toBeDefined(); + }); +}); + +function makeTaxonomy(): TaxonomyQuery { + const taxonomy = stack.taxonomy(); + + return taxonomy; +} diff --git a/test/api/types.ts b/test/api/types.ts index 776e3b2c..11197b6f 100644 --- a/test/api/types.ts +++ b/test/api/types.ts @@ -86,3 +86,20 @@ export interface TContentType { export interface TContentTypes { content_types: TContentType[]; } + +export interface TTaxonomies { + taxonomies: TTaxonomy[]; +} + +export interface TTaxonomy { + uid: string; + name: string; + description?: string; + terms_count: number; + created_at: string; + updated_at: string; + created_by: string; + updated_by: string; + type: string; + publish_details: PublishDetails; +} \ No newline at end of file diff --git a/test/unit/taxonomy.spec.ts b/test/unit/taxonomy.spec.ts new file mode 100644 index 00000000..5d37ac4e --- /dev/null +++ b/test/unit/taxonomy.spec.ts @@ -0,0 +1,26 @@ +import { TaxonomyQuery } from '../../src/lib/taxonomy-query'; +import { AxiosInstance, httpClient } from '@contentstack/core'; +import MockAdapter from 'axios-mock-adapter'; +import { taxonomyFindResponseDataMock } from '../utils/mocks'; +import { MOCK_CLIENT_OPTIONS } from '../utils/constant'; + +describe('ta class', () => { + let taxonomy: TaxonomyQuery; + let client: AxiosInstance; + let mockClient: MockAdapter; + + beforeAll(() => { + client = httpClient(MOCK_CLIENT_OPTIONS); + mockClient = new MockAdapter(client as any); + }); + + beforeEach(() => { + taxonomy = new TaxonomyQuery(client); + }); + + it('should return response data when successful', async () => { + mockClient.onGet('/taxonomy-manager').reply(200, taxonomyFindResponseDataMock); //TODO: change to /taxonomies + const response = await taxonomy.find(); + expect(response).toEqual(taxonomyFindResponseDataMock); + }); +}); diff --git a/test/utils/mocks.ts b/test/utils/mocks.ts index a265d0cd..27e79be3 100644 --- a/test/utils/mocks.ts +++ b/test/utils/mocks.ts @@ -1676,6 +1676,29 @@ const gfieldQueryFindResponseDataMock = { ] } +const taxonomyFindResponseDataMock = { + "taxonomies": [ + { + "uid": "taxonomy_testing", + "name": "taxonomy testing", + "description": "", + "terms_count": 1, + "created_at": "2025-10-10T06:42:48.644Z", + "updated_at": "2025-10-10T06:42:48.644Z", + "created_by": "created_by", + "updated_by": "updated_by", + "type": "TAXONOMY", + "ACL": {}, + "publish_details": { + "time": "2025-10-10T08:01:48.174Z", + "user": "user", + "environment": "env", + "locale": "en-us" + } + } + ] +} + const syncResult: any = { ...axiosGetMock.data }; export { @@ -1688,5 +1711,6 @@ export { entryFindMock, entryFetchMock, gfieldFetchDataMock, - gfieldQueryFindResponseDataMock + gfieldQueryFindResponseDataMock, + taxonomyFindResponseDataMock };