Skip to content

Commit 75c7d9a

Browse files
Merge pull request #249 from contentstack/feat/dx-3633-descendants
feat: Add descendants method to Term class and update related tests
2 parents 7a8b8a4 + 6a8122f commit 75c7d9a

10 files changed

Lines changed: 234 additions & 13 deletions

File tree

src/lib/taxonomy-query.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export class TaxonomyQuery extends Query {
1111
/**
1212
* @method find
1313
* @memberof TaxonomyQuery
14-
* @description Fetches all taxonomies of the stack using /taxonomy-manager endpoint
14+
* @description Fetches a list of all published taxonomies available in the stack.
1515
* @returns {Promise<FindResponse<T>>}
1616
* @example
1717
* import contentstack from '@contentstack/delivery-sdk'

src/lib/taxonomy.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,43 @@ import { AxiosInstance, getData } from '@contentstack/core';
22
import { TermQuery } from './term-query';
33
import { Term } from './term';
44

5+
/**
6+
* @class Taxonomy
7+
* @description Represents a published taxonomy with methods to fetch taxonomy data and manage terms. Requires taxonomy_publish feature flag to be enabled.
8+
*/
59
export class Taxonomy {
610
private _client: AxiosInstance;
711
private _taxonomyUid: string;
812
private _urlPath: string;
913

1014
_queryParams: { [key: string]: string | number } = {};
1115

16+
/**
17+
* @constructor
18+
* @param {AxiosInstance} client - The HTTP client instance
19+
* @param {string} taxonomyUid - The taxonomy UID
20+
*/
1221
constructor(client: AxiosInstance, taxonomyUid: string) {
1322
this._client = client;
1423
this._taxonomyUid = taxonomyUid;
1524
this._urlPath = `/taxonomy-manager/${this._taxonomyUid}`; // TODO: change to /taxonomies/${this._taxonomyUid}
1625
}
1726

27+
/**
28+
* @method term
29+
* @memberof Taxonomy
30+
* @description Gets a specific term or creates a term query
31+
* @param {string} [uid] - Optional term UID. If provided, returns a Term instance. If not provided, returns a TermQuery instance.
32+
* @returns {Term | TermQuery}
33+
* @example
34+
* import contentstack from '@contentstack/delivery-sdk'
35+
*
36+
* const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
37+
* // Get a specific term
38+
* const term = stack.taxonomy('taxonomy_uid').term('term_uid');
39+
* // Get all terms
40+
* const termQuery = stack.taxonomy('taxonomy_uid').term();
41+
*/
1842
term(uid: string): Term;
1943
term(): TermQuery;
2044
term(uid?: string): Term | TermQuery {
@@ -23,6 +47,17 @@ export class Taxonomy {
2347
return new TermQuery(this._client, this._taxonomyUid);
2448
}
2549

50+
/**
51+
* @method fetch
52+
* @memberof Taxonomy
53+
* @description Fetches the taxonomy data by UID
54+
* @returns {Promise<T>}
55+
* @example
56+
* import contentstack from '@contentstack/delivery-sdk'
57+
*
58+
* const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
59+
* const result = await stack.taxonomy('taxonomy_uid').fetch();
60+
*/
2661
async fetch<T>(): Promise<T> {
2762
const response = await getData(this._client, this._urlPath);
2863

src/lib/term-query.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,38 @@
11
import { AxiosInstance, getData } from '@contentstack/core';
22
import { FindResponse } from './types';
33

4+
/**
5+
* @class TermQuery
6+
* @description Represents a query for fetching multiple published terms from a taxonomy. Requires taxonomy_publish feature flag to be enabled.
7+
*/
48
export class TermQuery {
59
private _taxonomyUid: string;
610
private _client: AxiosInstance;
711
private _urlPath: string;
812
_queryParams: { [key: string]: string | number } = {};
913

14+
/**
15+
* @constructor
16+
* @param {AxiosInstance} client - The HTTP client instance
17+
* @param {string} taxonomyUid - The taxonomy UID
18+
*/
1019
constructor(client: AxiosInstance, taxonomyUid: string) {
1120
this._client = client;
1221
this._taxonomyUid = taxonomyUid;
1322
this._urlPath = `/taxonomy-manager/${this._taxonomyUid}/terms`;
1423
}
1524

25+
/**
26+
* @method find
27+
* @memberof TermQuery
28+
* @description Fetches a list of all published terms within a specific taxonomy.
29+
* @returns {Promise<FindResponse<T>>}
30+
* @example
31+
* import contentstack from '@contentstack/delivery-sdk'
32+
*
33+
* const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
34+
* const result = await stack.taxonomy('taxonomy_uid').term().find();
35+
*/
1636
async find<T>(): Promise<FindResponse<T>> {
1737
const response = await getData(this._client, this._urlPath, { params: this._queryParams });
1838
return response as FindResponse<T>;

src/lib/term.ts

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
import { AxiosInstance, getData } from "@contentstack/core";
22

3+
/**
4+
* @class Term
5+
* @description Represents a published taxonomy term with methods to fetch term data, locales, ancestors, and descendants. Requires taxonomy_publish feature flag to be enabled.
6+
*/
37
export class Term {
48
protected _client: AxiosInstance;
59
private _taxonomyUid: string;
610
private _termUid: string;
711
private _urlPath: string;
812

13+
/**
14+
* @constructor
15+
* @param {AxiosInstance} client - The HTTP client instance
16+
* @param {string} taxonomyUid - The taxonomy UID
17+
* @param {string} termUid - The term UID
18+
*/
919
constructor(client: AxiosInstance, taxonomyUid: string, termUid: string) {
1020
this._client = client;
1121
this._taxonomyUid = taxonomyUid;
@@ -16,7 +26,7 @@ export class Term {
1626
/**
1727
* @method locales
1828
* @memberof Term
19-
* @description Fetches locales for the term
29+
* @description Fetches all published, localized versions of a single term.
2030
* @returns {Promise<T>}
2131
* @example
2232
* import contentstack from '@contentstack/delivery-sdk'
@@ -33,7 +43,7 @@ export class Term {
3343
/**
3444
* @method ancestors
3545
* @memberof Term
36-
* @description Fetches ancestors for the term
46+
* @description Fetches all ancestors of a single published term, up to the root.
3747
* @returns {Promise<T>}
3848
* @example
3949
* import contentstack from '@contentstack/delivery-sdk'
@@ -47,11 +57,37 @@ export class Term {
4757
return response;
4858
}
4959

60+
/**
61+
* @method descendants
62+
* @memberof Term
63+
* @description Fetches all descendants of a single published term.
64+
* @returns {Promise<T>}
65+
* @example
66+
* import contentstack from '@contentstack/delivery-sdk'
67+
*
68+
* const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
69+
* const result = await stack.taxonomy('taxonomy_uid').term('term_uid').descendants();
70+
*/
71+
async descendants<T>(): Promise<T> {
72+
const response = await getData(this._client, `${this._urlPath}/descendants`);
73+
if (response.descendants) return response.descendants as T;
74+
return response;
75+
}
76+
77+
/**
78+
* @method fetch
79+
* @memberof Term
80+
* @description Fetches all descendants of a single published term.
81+
* @returns {Promise<T>}
82+
* @example
83+
* import contentstack from '@contentstack/delivery-sdk'
84+
*
85+
* const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
86+
* const result = await stack.taxonomy('taxonomy_uid').term('term_uid').fetch();
87+
*/
5088
async fetch<T>(): Promise<T> {
5189
const response = await getData(this._client, this._urlPath);
52-
5390
if (response.term) return response.term as T;
54-
5591
return response;
5692
}
5793
}

src/lib/types.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,25 @@ export type queryParams = {
1414

1515
/**
1616
* Interface for creating Contentstack plugins
17-
*
17+
*
1818
* @example
1919
* ```typescript
2020
* import { ContentstackPlugin } from '@contentstack/delivery-sdk';
21-
*
21+
*
2222
* class MyPlugin implements ContentstackPlugin {
2323
* onRequest(config: any): any {
2424
* // Modify request configuration
2525
* console.log('Processing request:', config.url);
2626
* return { ...config, headers: { ...config.headers, 'X-Custom-Header': 'value' } };
2727
* }
28-
*
28+
*
2929
* onResponse(request: any, response: any, data: any): any {
3030
* // Process response data
3131
* console.log('Processing response:', response.status);
3232
* return { ...response, data: { ...data, processed: true } };
3333
* }
3434
* }
35-
*
35+
*
3636
* const stack = contentstack.stack({
3737
* apiKey: 'your-api-key',
3838
* deliveryToken: 'your-delivery-token',
@@ -342,3 +342,31 @@ export type LivePreview = {
342342
management_token?: string;
343343
preview_token?: string;
344344
};
345+
346+
export interface BaseTaxonomy {
347+
uid: string;
348+
name: string;
349+
description?: string;
350+
terms_count?: number;
351+
created_at: string;
352+
updated_at: string;
353+
created_by: string;
354+
updated_by: string;
355+
type: string;
356+
ACL: ACL;
357+
publish_details?: PublishDetails;
358+
}
359+
360+
export interface BaseTerm {
361+
taxonomy_uid: string;
362+
uid: string;
363+
name: string;
364+
created_by: string;
365+
created_at: string;
366+
updated_by: string;
367+
updated_at: string;
368+
children_count?: number;
369+
depth?: number;
370+
ACL: ACL;
371+
publish_details?: PublishDetails;
372+
}

test/api/taxonomy.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ describe('ContentType API test cases', () => {
1616
});
1717

1818
it('should give a single taxonomy when taxonomy method is called with taxonomyUid', async () => {
19-
const result = await makeTaxonomy('taxonomy_testing_3').fetch<TTaxonomy>();
19+
const result = await makeTaxonomy('taxonomy_testing').fetch<TTaxonomy>();
2020
expect(result).toBeDefined();
2121
});
2222
});

test/api/term-query.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const stack = stackInstance();
66

77
describe("Terms API test cases", () => {
88
it("should check for terms is defined", async () => {
9-
const result = await makeTerms("taxonomy_testing_3").find<TTerm>();
9+
const result = await makeTerms("taxonomy_testing").find<TTerm>();
1010
if (result.terms) {
1111
expect(result.terms).toBeDefined();
1212
expect(result.terms[0].taxonomy_uid).toBeDefined();

test/api/term.spec.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,16 @@ describe("Terms API test cases", () => {
2727
expect(result.terms).toBeDefined();
2828
expect(result.terms[0].name).toBeDefined();
2929
});
30+
31+
it("should get descendants for a term", async () => {
32+
const result = await makeTerms("vrl").descendants<TTerms>();
33+
expect(result).toBeDefined();
34+
expect(result.terms).toBeDefined();
35+
expect(result.terms[0].name).toBeDefined();
36+
});
3037
});
3138

3239
function makeTerms(termUid = ""): Term {
33-
const terms = stack.taxonomy("taxonomy_testing_3").term(termUid);
40+
const terms = stack.taxonomy("taxonomy_testing").term(termUid);
3441
return terms;
3542
}

test/unit/term.spec.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { AxiosInstance, httpClient } from '@contentstack/core';
22
import MockAdapter from 'axios-mock-adapter';
3-
import { termQueryFindResponseDataMock, termLocalesResponseDataMock, termAncestorsResponseDataMock } from '../utils/mocks';
3+
import { termQueryFindResponseDataMock, termLocalesResponseDataMock, termAncestorsResponseDataMock, termDescendantsResponseDataMock } from '../utils/mocks';
44
import { MOCK_CLIENT_OPTIONS } from '../utils/constant';
55
import { Term } from '../../src/lib/term';
66
import { Taxonomy } from '../../src/lib/taxonomy';
@@ -39,4 +39,11 @@ describe('Term class', () => {
3939
const response = await term.ancestors();
4040
expect(response).toEqual(termAncestorsResponseDataMock);
4141
});
42+
43+
it('should fetch descendants for a term when descendants() is called', async () => {
44+
mockClient.onGet('/taxonomy-manager/taxonomy_testing/terms/term1/descendants').reply(200, termDescendantsResponseDataMock);
45+
46+
const response = await term.descendants();
47+
expect(response).toEqual(termDescendantsResponseDataMock);
48+
});
4249
});

0 commit comments

Comments
 (0)