From c213dd86632672bdce544603460bc9e769db475a Mon Sep 17 00:00:00 2001 From: "harshitha.d" Date: Thu, 23 Oct 2025 17:01:07 +0530 Subject: [PATCH 1/2] feat: Add Term and TermQuery classes for enhanced taxonomy term management --- src/lib/taxonomy.ts | 10 ++++++++++ src/lib/term-query.ts | 20 ++++++++++++++++++++ src/lib/term.ts | 22 ++++++++++++++++++++++ src/lib/types.ts | 2 ++ test/api/term-query.spec.ts | 23 +++++++++++++++++++++++ test/api/term.spec.ts | 20 ++++++++++++++++++++ test/api/types.ts | 22 ++++++++++++++++++++-- test/unit/taxonomy.spec.ts | 14 +++++++++++++- test/unit/term-query.spec.ts | 26 ++++++++++++++++++++++++++ test/unit/term.spec.ts | 28 ++++++++++++++++++++++++++++ test/utils/mocks.ts | 32 +++++++++++++++++++++++++++++++- 11 files changed, 215 insertions(+), 4 deletions(-) create mode 100644 src/lib/term-query.ts create mode 100644 src/lib/term.ts create mode 100644 test/api/term-query.spec.ts create mode 100644 test/api/term.spec.ts create mode 100644 test/unit/term-query.spec.ts create mode 100644 test/unit/term.spec.ts diff --git a/src/lib/taxonomy.ts b/src/lib/taxonomy.ts index 6e7a8745..84b05072 100644 --- a/src/lib/taxonomy.ts +++ b/src/lib/taxonomy.ts @@ -1,4 +1,6 @@ import { AxiosInstance, getData } from '@contentstack/core'; +import { TermQuery } from './term-query'; +import { Term } from './term'; export class Taxonomy { private _client: AxiosInstance; @@ -13,6 +15,14 @@ export class Taxonomy { this._urlPath = `/taxonomy-manager/${this._taxonomyUid}`; // TODO: change to /taxonomies/${this._taxonomyUid} } + term(uid: string): Term; + term(): TermQuery; + term(uid?: string): Term | TermQuery { + if (uid) return new Term(this._client, this._taxonomyUid, uid); + + return new TermQuery(this._client, this._taxonomyUid); + } + async fetch(): Promise { const response = await getData(this._client, this._urlPath); diff --git a/src/lib/term-query.ts b/src/lib/term-query.ts new file mode 100644 index 00000000..23a8698f --- /dev/null +++ b/src/lib/term-query.ts @@ -0,0 +1,20 @@ +import { AxiosInstance, getData } from '@contentstack/core'; +import { FindResponse } from './types'; + +export class TermQuery { + private _taxonomyUid: string; + private _client: AxiosInstance; + 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}/terms`; + } + + async find(): Promise> { + const response = await getData(this._client, this._urlPath, { params: this._queryParams }); + return response as FindResponse; + } +} diff --git a/src/lib/term.ts b/src/lib/term.ts new file mode 100644 index 00000000..615e95f5 --- /dev/null +++ b/src/lib/term.ts @@ -0,0 +1,22 @@ +import { AxiosInstance, getData } from "@contentstack/core"; + +export class Term { + protected _client: AxiosInstance; + private _taxonomyUid: string; + private _termUid: string; + private _urlPath: string; + + constructor(client: AxiosInstance, taxonomyUid: string, termUid: string) { + this._client = client; + this._taxonomyUid = taxonomyUid; + this._termUid = termUid; + this._urlPath = `/taxonomy-manager/${this._taxonomyUid}/terms/${this._termUid}`; // TODO: change to /taxonomies + } + async fetch(): Promise { + const response = await getData(this._client, this._urlPath); + + if (response.term) return response.term as T; + + return response; + } +} diff --git a/src/lib/types.ts b/src/lib/types.ts index e19a0326..a5106675 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -317,6 +317,8 @@ export interface FindResponse { assets?: T[]; global_fields?: T[]; count?: number; + taxonomies?: T[]; + terms?: T[]; } export interface LivePreviewQuery { diff --git a/test/api/term-query.spec.ts b/test/api/term-query.spec.ts new file mode 100644 index 00000000..58f59574 --- /dev/null +++ b/test/api/term-query.spec.ts @@ -0,0 +1,23 @@ +import { TermQuery } from "../../src/lib/term-query"; +import { stackInstance } from "../utils/stack-instance"; +import { TTerm } from "./types"; + +const stack = stackInstance(); + +describe("Terms API test cases", () => { + it("should check for terms is defined", async () => { + const result = await makeTerms("taxonomy_testing").find(); + if (result.terms) { + expect(result.terms).toBeDefined(); + expect(result.terms[0].taxonomy_uid).toBeDefined(); + expect(result.terms[0].uid).toBeDefined(); + expect(result.terms[0].created_by).toBeDefined(); + expect(result.terms[0].updated_by).toBeDefined(); + } + }); +}); +function makeTerms(taxonomyUid = ""): TermQuery { + const terms = stack.taxonomy(taxonomyUid).term(); + + return terms; +} diff --git a/test/api/term.spec.ts b/test/api/term.spec.ts new file mode 100644 index 00000000..72c1c6eb --- /dev/null +++ b/test/api/term.spec.ts @@ -0,0 +1,20 @@ +import { Term } from "../../src/lib/term"; +import { stackInstance } from "../utils/stack-instance"; +import { TTerm } from "./types"; + +const stack = stackInstance(); + +describe("Terms API test cases", () => { + it("should get a term by uid", async () => { + const result = await makeTerms("term1").fetch(); + expect(result).toBeDefined(); + expect(result.taxonomy_uid).toBeDefined(); + expect(result.uid).toBeDefined(); + expect(result.created_by).toBeDefined(); + expect(result.updated_by).toBeDefined(); + }); +}); +function makeTerms(termUid = ""): Term { + const terms = stack.taxonomy("taxonomy_testing").term(termUid); + return terms; +} diff --git a/test/api/types.ts b/test/api/types.ts index 11197b6f..f59f73cd 100644 --- a/test/api/types.ts +++ b/test/api/types.ts @@ -95,11 +95,29 @@ export interface TTaxonomy { uid: string; name: string; description?: string; - terms_count: number; + terms_count?: number; created_at: string; updated_at: string; created_by: string; updated_by: string; type: string; - publish_details: PublishDetails; + publish_details?: PublishDetails; +} + +export interface TTerms { + terms: TTerm[]; +} + +export interface TTerm { + taxonomy_uid: string; + uid: string; + ancestors: TTerm[]; + name: string; + created_by: string; + created_at: string; + updated_by: string; + updated_at: string; + children_count?: number; + depth?: number; + publish_details?: PublishDetails; } \ No newline at end of file diff --git a/test/unit/taxonomy.spec.ts b/test/unit/taxonomy.spec.ts index 0c4670e5..5db38cb6 100644 --- a/test/unit/taxonomy.spec.ts +++ b/test/unit/taxonomy.spec.ts @@ -4,6 +4,8 @@ import { AxiosInstance, httpClient } from '@contentstack/core'; import MockAdapter from 'axios-mock-adapter'; import { taxonomyFindResponseDataMock } from '../utils/mocks'; import { MOCK_CLIENT_OPTIONS } from '../utils/constant'; +import { Term } from '../../src/lib/term'; +import { TermQuery } from '../../src/lib/term-query'; describe('ta class', () => { let taxonomies: TaxonomyQuery; @@ -21,6 +23,16 @@ describe('ta class', () => { taxonomy = new Taxonomy(client, 'taxonomy_testing'); }); + it('should give term instance when term method is called with termUid', () => { + const query = taxonomy.term('termUid'); + expect(query).toBeInstanceOf(Term); + }); + + it('should give term query instance when term method is called without termUid', () => { + const query = taxonomy.term() + expect(query).toBeInstanceOf(TermQuery); + }); + it('should return all taxonomies in the response data when successful', async () => { mockClient.onGet('/taxonomy-manager').reply(200, taxonomyFindResponseDataMock); //TODO: change to /taxonomies const response = await taxonomies.find(); @@ -30,6 +42,6 @@ describe('ta class', () => { it('should return single taxonomy in the response data when successful', async () => { mockClient.onGet('/taxonomy-manager/taxonomy_testing').reply(200, taxonomyFindResponseDataMock.taxonomies[0]); //TODO: change to /taxonomies/taxonomyUid const response = await taxonomy.fetch(); - expect(response).toEqual(taxonomyFindResponseDataMock.taxonomies[0]); //TODO: change to taxonomyFindResponseDataMock + expect(response).toEqual(taxonomyFindResponseDataMock.taxonomies[0]); }); }); diff --git a/test/unit/term-query.spec.ts b/test/unit/term-query.spec.ts new file mode 100644 index 00000000..ea00e0a4 --- /dev/null +++ b/test/unit/term-query.spec.ts @@ -0,0 +1,26 @@ +import { TermQuery } from '../../src/lib/term-query'; +import { AxiosInstance, httpClient } from '@contentstack/core'; +import MockAdapter from 'axios-mock-adapter'; +import { TermQueryFindResponseDataMock } from '../utils/mocks'; +import { MOCK_CLIENT_OPTIONS } from '../utils/constant'; + +describe('TermQuery class', () => { + let termQuery: TermQuery; + let client: AxiosInstance; + let mockClient: MockAdapter; + + beforeAll(() => { + client = httpClient(MOCK_CLIENT_OPTIONS); + mockClient = new MockAdapter(client as any); + }); + + beforeEach(() => { + termQuery = new TermQuery(client, 'taxonomy_testing'); + }); + + it('should return response data when successful', async () => { + mockClient.onGet('/taxonomy-manager/taxonomy_testing/terms').reply(200, TermQueryFindResponseDataMock); + const response = await termQuery.find(); + expect(response).toEqual(TermQueryFindResponseDataMock); + }); +}); diff --git a/test/unit/term.spec.ts b/test/unit/term.spec.ts new file mode 100644 index 00000000..d1e34c9d --- /dev/null +++ b/test/unit/term.spec.ts @@ -0,0 +1,28 @@ +import { AxiosInstance, httpClient } from '@contentstack/core'; +import MockAdapter from 'axios-mock-adapter'; +import { TermQueryFindResponseDataMock } from '../utils/mocks'; +import { MOCK_CLIENT_OPTIONS } from '../utils/constant'; +import { Term } from '../../src/lib/term'; +import { Taxonomy } from '../../src/lib/taxonomy'; + +describe('Term class', () => { + let term: Term; + let client: AxiosInstance; + let mockClient: MockAdapter; + + beforeAll(() => { + client = httpClient(MOCK_CLIENT_OPTIONS); + mockClient = new MockAdapter(client as any); + }); + + beforeEach(() => { + term = new Term(client, 'taxonomy_testing', 'term1'); + }); + + it('should fetch the term by uid response when fetch method is called', async () => { + mockClient.onGet('/taxonomy-manager/taxonomy_testing/terms/term1').reply(200, TermQueryFindResponseDataMock.terms[0]); //TODO: change to /taxonomies + + const response = await term.fetch(); + expect(response).toEqual(TermQueryFindResponseDataMock.terms[0]); + }); +}); diff --git a/test/utils/mocks.ts b/test/utils/mocks.ts index 27e79be3..15fa28a0 100644 --- a/test/utils/mocks.ts +++ b/test/utils/mocks.ts @@ -1698,6 +1698,35 @@ const taxonomyFindResponseDataMock = { } ] } +const TermQueryFindResponseDataMock = { + "terms": [ + { + "taxonomy_uid": "taxonomy_testing", + "uid": "term1", + "ancestors": [ + { + "uid": "taxonomy_testing", + "name": "taxonomy testing", + "type": "TAXONOMY" + } + ], + "name": "term1", + "created_by": "created_by", + "created_at": "2025-10-10T06:43:13.799Z", + "updated_by": "updated_by", + "updated_at": "2025-10-10T06:43:13.799Z", + "children_count": 0, + "depth": 1, + "ACL": {}, + "publish_details": { + "time": "2025-10-10T08:01:48.351Z", + "user": "user", + "environment": "environment", + "locale": "en-us" + } + } +] +} const syncResult: any = { ...axiosGetMock.data }; @@ -1712,5 +1741,6 @@ export { entryFetchMock, gfieldFetchDataMock, gfieldQueryFindResponseDataMock, - taxonomyFindResponseDataMock + taxonomyFindResponseDataMock, + TermQueryFindResponseDataMock, }; From b7e629ab4776b34b950814b5fb59868c1e7d147c Mon Sep 17 00:00:00 2001 From: "harshitha.d" Date: Thu, 23 Oct 2025 17:03:10 +0530 Subject: [PATCH 2/2] refactor: Rename TermQueryFindResponseDataMock to termQueryFindResponseDataMock for consistency in tests --- test/unit/term-query.spec.ts | 6 +++--- test/unit/term.spec.ts | 6 +++--- test/utils/mocks.ts | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/unit/term-query.spec.ts b/test/unit/term-query.spec.ts index ea00e0a4..a96019d0 100644 --- a/test/unit/term-query.spec.ts +++ b/test/unit/term-query.spec.ts @@ -1,7 +1,7 @@ import { TermQuery } from '../../src/lib/term-query'; import { AxiosInstance, httpClient } from '@contentstack/core'; import MockAdapter from 'axios-mock-adapter'; -import { TermQueryFindResponseDataMock } from '../utils/mocks'; +import { termQueryFindResponseDataMock } from '../utils/mocks'; import { MOCK_CLIENT_OPTIONS } from '../utils/constant'; describe('TermQuery class', () => { @@ -19,8 +19,8 @@ describe('TermQuery class', () => { }); it('should return response data when successful', async () => { - mockClient.onGet('/taxonomy-manager/taxonomy_testing/terms').reply(200, TermQueryFindResponseDataMock); + mockClient.onGet('/taxonomy-manager/taxonomy_testing/terms').reply(200, termQueryFindResponseDataMock); const response = await termQuery.find(); - expect(response).toEqual(TermQueryFindResponseDataMock); + expect(response).toEqual(termQueryFindResponseDataMock); }); }); diff --git a/test/unit/term.spec.ts b/test/unit/term.spec.ts index d1e34c9d..65d4140e 100644 --- a/test/unit/term.spec.ts +++ b/test/unit/term.spec.ts @@ -1,6 +1,6 @@ import { AxiosInstance, httpClient } from '@contentstack/core'; import MockAdapter from 'axios-mock-adapter'; -import { TermQueryFindResponseDataMock } from '../utils/mocks'; +import { termQueryFindResponseDataMock } from '../utils/mocks'; import { MOCK_CLIENT_OPTIONS } from '../utils/constant'; import { Term } from '../../src/lib/term'; import { Taxonomy } from '../../src/lib/taxonomy'; @@ -20,9 +20,9 @@ describe('Term class', () => { }); it('should fetch the term by uid response when fetch method is called', async () => { - mockClient.onGet('/taxonomy-manager/taxonomy_testing/terms/term1').reply(200, TermQueryFindResponseDataMock.terms[0]); //TODO: change to /taxonomies + mockClient.onGet('/taxonomy-manager/taxonomy_testing/terms/term1').reply(200, termQueryFindResponseDataMock.terms[0]); //TODO: change to /taxonomies const response = await term.fetch(); - expect(response).toEqual(TermQueryFindResponseDataMock.terms[0]); + expect(response).toEqual(termQueryFindResponseDataMock.terms[0]); }); }); diff --git a/test/utils/mocks.ts b/test/utils/mocks.ts index 15fa28a0..82c4becc 100644 --- a/test/utils/mocks.ts +++ b/test/utils/mocks.ts @@ -1698,7 +1698,7 @@ const taxonomyFindResponseDataMock = { } ] } -const TermQueryFindResponseDataMock = { +const termQueryFindResponseDataMock = { "terms": [ { "taxonomy_uid": "taxonomy_testing", @@ -1742,5 +1742,5 @@ export { gfieldFetchDataMock, gfieldQueryFindResponseDataMock, taxonomyFindResponseDataMock, - TermQueryFindResponseDataMock, + termQueryFindResponseDataMock, };