diff --git a/lib/create-contentful-api.ts b/lib/create-contentful-api.ts index 345259205..c91683dc6 100644 --- a/lib/create-contentful-api.ts +++ b/lib/create-contentful-api.ts @@ -38,6 +38,7 @@ import { validateResolveLinksParam, } from './utils/validate-params.js' import validateSearchParameters from './utils/validate-search-parameters.js' +import { ConceptCollection } from './types/taxonomy.js' const ASSET_KEY_MAX_LIFETIME = 48 * 60 * 60 @@ -467,6 +468,26 @@ export default function createContentfulApi( }) } + function getConcepts( + query: Record = {}, + ): Promise> { + return internalGetConcepts>(query) + } + + async function internalGetConcepts(query: Record = {}): Promise { + try { + return get({ + context: 'environment', + path: 'taxonomy/concepts', + config: createRequestConfig({ + query: normalizeSearchParameters(normalizeSelect(query)), + }), + }) + } catch (error) { + errorHandler(error) + } + } + /* * Switches BaseURL to use /environments path * */ @@ -496,5 +517,7 @@ export default function createContentfulApi( getEntries, createAssetKey, + + getConcepts, } as unknown as ContentfulClientApi } diff --git a/lib/types/client.ts b/lib/types/client.ts index 14192d2cd..f097b38c9 100644 --- a/lib/types/client.ts +++ b/lib/types/client.ts @@ -4,6 +4,7 @@ import { LocaleCode, LocaleCollection } from './locale.js' import { AssetQueries, AssetsQueries, + ConceptQueries, EntriesQueries, EntryQueries, EntrySkeletonType, @@ -14,6 +15,7 @@ import { Tag, TagCollection } from './tag.js' import { AssetKey } from './asset-key.js' import { Entry, EntryCollection } from './entry.js' import { Asset, AssetCollection, AssetFields } from './asset.js' +import { ConceptCollection } from './taxonomy.js' /** * Client chain modifiers used in all types that depend on the client configuration. @@ -364,6 +366,25 @@ export interface ContentfulClientApi { query?: AssetsQueries, ): Promise> + /** + * Fetches a collection of Taxonomy Concepts + * @param query - Query object for filtering and ordering + * @returns Promise for a collection of concepts + * @example + * const contentful = require('contentful') + * + * const client = contentful.createClient({ + * space: '', + * accessToken: '' + * }) + * + * const response = await client.getConcepts() + * console.log(response.items) + */ + getConcepts( + query?: ConceptQueries, + ): Promise> + /** * A client that will fetch assets and entries with all locales. Only available if not already enabled. */ diff --git a/lib/types/query/query.ts b/lib/types/query/query.ts index 9c819a2ff..288b495e9 100644 --- a/lib/types/query/query.ts +++ b/lib/types/query/query.ts @@ -4,6 +4,7 @@ import { EntrySys } from '../entry.js' import { TagLink, TaxonomyConceptLink } from '../link.js' import { Metadata } from '../metadata.js' import { TagSys } from '../tag.js' +import { ConceptSys } from '../taxonomy.js' import { EntryFieldsEqualityFilter, EntryFieldsInequalityFilter, @@ -219,3 +220,9 @@ export type TagQueries = TagNameFilters & SysQueries> & TagOrderFilter & FixedPagedOptions + +/** + * Search parameters for taxonomy methods + */ + +export type ConceptQueries = SysQueries> diff --git a/lib/types/taxonomy.ts b/lib/types/taxonomy.ts new file mode 100644 index 000000000..1df65ec92 --- /dev/null +++ b/lib/types/taxonomy.ts @@ -0,0 +1,36 @@ +import { ContentfulCollection } from './collection' +import { Link } from './link' + +type ISODateString = string + +export type ConceptSys = { + id: string + type: 'TaxonomyConcept' + createdAt: ISODateString + updatedAt: ISODateString + version: number +} + +export interface Concept { + sys: ConceptSys + prefLabel?: { + [locale in Locales]: string + } + altLabels?: { + [locale in Locales]: string + } + hiddenLabels?: { + [locale in Locales]: string + } + note?: { + [locale in Locales]: string + } + notations?: string[] + broader?: Link<'TaxonomyConcept'>[] + related?: Link<'TaxonomyConcept'>[] +} + +export interface ConceptScheme {} + +export type ConceptCollection = ContentfulCollection> +export type ConceptSchemeCollection = ContentfulCollection diff --git a/test/integration/getConcepts.test.ts b/test/integration/getConcepts.test.ts new file mode 100644 index 000000000..5794e027c --- /dev/null +++ b/test/integration/getConcepts.test.ts @@ -0,0 +1,66 @@ +import * as contentful from '../../lib/contentful' +// import { ValidationError } from '../../lib/utils/validation-error' +import { + // assetMappings, + // localisedAssetMappings, + params, + // previewParamsWithCSM, + // testEncodingDecoding, +} from './utils' + +if (process.env.API_INTEGRATION_TESTS) { + params.host = '127.0.0.1:5000' + params.insecure = true +} + +const client = contentful.createClient(params) +// const invalidClient = contentful.createClient({ +// ...params, +// includeContentSourceMaps: true, +// }) +// const previewClient = contentful.createClient(previewParamsWithCSM) + +describe.only('getConcepts', () => { + test('default client', async () => { + const response = await client.getConcepts() + + expect(response.items).toBeDefined() + expect(typeof response.items[0].sys.id).toBe('string') + }) + + // test('client has withAllLocales modifier', async () => { + // const response = await client.withAllLocales.getAsset(asset) + + // expect(response.fields).toBeDefined() + // expect(typeof response.fields.title).toBe('object') + // }) + + // describe('has includeContentSourceMaps enabled', () => { + // test('cdn client', async () => { + // await expect(invalidClient.getAsset(asset)).rejects.toThrow( + // `The 'includeContentSourceMaps' parameter can only be used with the CPA. Please set host to 'preview.contentful.com' to include Content Source Maps.`, + // ) + // await expect(invalidClient.getAsset(asset)).rejects.toThrow(ValidationError) + // }) + + // test('preview client', async () => { + // const response = await previewClient.getAsset(asset) + + // expect(response.fields).toBeDefined() + // expect(typeof response.fields.title).toBe('string') + // expect(response.sys.contentSourceMaps).toBeDefined() + // expect(response.sys?.contentSourceMapsLookup).toBeDefined() + // testEncodingDecoding(response, assetMappings) + // }) + + // test('preview client withAllLocales modifier', async () => { + // const response = await previewClient.withAllLocales.getAsset(asset) + + // expect(response.fields).toBeDefined() + // expect(typeof response.fields.title).toBe('object') + // expect(response.sys.contentSourceMaps).toBeDefined() + // expect(response.sys?.contentSourceMapsLookup).toBeDefined() + // testEncodingDecoding(response, localisedAssetMappings) + // }) + // }) +})