diff --git a/CHANGELOG.md b/CHANGELOG.md index fd6b8f3b3c..0625b5a832 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ Our versioning strategy is as follows: * `[sitecore-jss]` GenericFieldValue model is updated to accept Date type ([#1916](https://github.com/Sitecore/jss/pull/1916)) * `[template/node-xmcloud-proxy]` `[sitecore-jss-proxy]` Introduced /api/healthz endpoint ([#1928](https://github.com/Sitecore/jss/pull/1928)) * `[sitecore-jss]` `[sitecore-jss-angular]` Render field metdata chromes in editMode metadata - in edit mode metadata in Pages, angular package field directives will render wrapping `code` elements with field metadata required for editing; ([#1926](https://github.com/Sitecore/jss/pull/1926)) +* `[sitecore-jss]` Added services for Content Tokens `/sitecore/templates/Feature/Experience Accelerator/Content Tokens/Content Token` so we're a step closer to enabling (custom) content replacement tokens in headless development ### 🛠 Breaking Change diff --git a/packages/sitecore-jss-nextjs/src/index.ts b/packages/sitecore-jss-nextjs/src/index.ts index bff971f82c..1ca2981304 100644 --- a/packages/sitecore-jss-nextjs/src/index.ts +++ b/packages/sitecore-jss-nextjs/src/index.ts @@ -48,10 +48,16 @@ export { PageViewInstance, } from '@sitecore-jss/sitecore-jss/tracking'; export { + ContentTokenPhrases, + ContentTokenService, DictionaryPhrases, DictionaryService, + GraphQLContentTokenService, + GraphQLContentTokenServiceConfig, GraphQLDictionaryService, GraphQLDictionaryServiceConfig, + // TODO:RestContentTokenService, + // TODO:RestContentTokenServiceConfig, RestDictionaryService, RestDictionaryServiceConfig, } from '@sitecore-jss/sitecore-jss/i18n'; diff --git a/packages/sitecore-jss/src/constants.ts b/packages/sitecore-jss/src/constants.ts index 295c116dcf..08f5746481 100644 --- a/packages/sitecore-jss/src/constants.ts +++ b/packages/sitecore-jss/src/constants.ts @@ -4,6 +4,9 @@ export enum SitecoreTemplateId { // /sitecore/templates/System/Dictionary/Dictionary entry DictionaryEntry = '6d1cd89719364a3aa511289a94c2a7b1', + + // /sitecore/templates/Feature/Experience Accelerator/Content Tokens/Content Token + ContentToken = '7d659ee9d4874d408a9210c6d68844c8', } export const FETCH_WITH = { diff --git a/packages/sitecore-jss/src/debug.ts b/packages/sitecore-jss/src/debug.ts index 692c342be8..da87446909 100644 --- a/packages/sitecore-jss/src/debug.ts +++ b/packages/sitecore-jss/src/debug.ts @@ -40,4 +40,5 @@ export default { personalize: debug(`${rootNamespace}:personalize`), errorpages: debug(`${rootNamespace}:errorpages`), proxy: debug(`${rootNamespace}:proxy`), + contenttokens: debug(`${rootNamespace}:contenttokens`), }; diff --git a/packages/sitecore-jss/src/i18n/content-token-service.ts b/packages/sitecore-jss/src/i18n/content-token-service.ts new file mode 100644 index 0000000000..458091d37d --- /dev/null +++ b/packages/sitecore-jss/src/i18n/content-token-service.ts @@ -0,0 +1,72 @@ +import { CacheClient, CacheOptions, MemoryCacheClient } from '../cache-client'; + +/** + * Object model for Sitecore dictionary phrases + */ +export interface ContentTokenPhrases { + [k: string]: string; +} + +/** + * Service that fetches dictionary data using Sitecore's GraphQL API. + */ +export interface ContentTokenService { + /** + * Fetch dictionary data for a language. + * @param {string} language the language to be used to fetch the dictionary + */ + fetchContentTokens(language: string): Promise; +} + +/** + * Base implementation of @see ContentTokenService that handles caching dictionary values + */ +export abstract class ContentTokenServiceBase + implements ContentTokenService, CacheClient { + private cache: CacheClient; + + /** + * Initializes a new instance of @see ContentTokenService using the provided @see CacheOptions + * @param {CacheOptions} options Configuration options + */ + constructor(public options: CacheOptions) { + this.cache = this.getCacheClient(); + } + + /** + * Caches a @see ContentTokenPhrases value for the specified cache key. + * @param {string} key The cache key. + * @param {ContentTokenPhrases} value The value to cache. + * @returns The value added to the cache. + * @mixes CacheClient + */ + setCacheValue(key: string, value: ContentTokenPhrases): ContentTokenPhrases { + return this.cache.setCacheValue(key, value); + } + + /** + * Retrieves a @see ContentTokenPhrases value from the cache. + * @param {string} key The cache key. + * @returns The @see ContentTokenPhrases value, or null if the specified key is not found in the cache. + */ + getCacheValue(key: string): ContentTokenPhrases | null { + return this.cache.getCacheValue(key); + } + + /** + * Gets a cache client that can cache data. Uses memory-cache as the default + * library for caching (@see MemoryCacheClient). Override this method if you + * want to use something else. + * @returns {CacheClient} implementation + */ + protected getCacheClient(): CacheClient { + return new MemoryCacheClient(this.options); + } + + /** + * Fetch dictionary data for a language. + * @param {string} language the language to be used to fetch the dictionary + * @returns {Promise} + */ + abstract fetchContentTokens(language: string): Promise; +} diff --git a/packages/sitecore-jss/src/i18n/graphql-content-token-service.test.ts b/packages/sitecore-jss/src/i18n/graphql-content-token-service.test.ts new file mode 100644 index 0000000000..33b9a32295 --- /dev/null +++ b/packages/sitecore-jss/src/i18n/graphql-content-token-service.test.ts @@ -0,0 +1,273 @@ +/* eslint-disable no-unused-expressions */ +import { expect } from 'chai'; +import sinon, { SinonSpy } from 'sinon'; +import nock from 'nock'; +import { SitecoreTemplateId } from '../constants'; +import { GraphQLClient, GraphQLRequestClient } from '../graphql-request-client'; +import { queryError, GraphQLContentTokenServiceConfig } from './graphql-content-token-service'; +import { GraphQLContentTokenService } from '.'; +import contentTokenQueryResponse from '../test-data/mockContentTokenQueryResponse.json'; +import appRootQueryResponse from '../test-data/mockAppRootQueryResponse.json'; + +class TestService extends GraphQLContentTokenService { + public client: GraphQLClient; + constructor(options: GraphQLContentTokenServiceConfig) { + super(options); + this.client = this.getGraphQLClient(); + } +} + +describe('GraphQLContentTokenService', () => { + const endpoint = 'http://site'; + const siteName = 'site-name'; + const apiKey = 'api-key'; + const rootItemId = '{GUID}'; + const clientFactory = GraphQLRequestClient.createClientFactory({ + endpoint, + apiKey, + }); + + afterEach(() => { + nock.cleanAll(); + }); + + it('should fetch content token phrases using clientFactory', async () => { + nock(endpoint, { reqheaders: { sc_apikey: apiKey } }) + .post('/', /ContentTokenSearch/gi) + .reply(200, contentTokenQueryResponse); + + const service = new GraphQLContentTokenService({ + siteName, + rootItemId, + cacheEnabled: false, + clientFactory, + }); + const result = await service.fetchContentTokens('en'); + expect(result.foo).to.equal('foo'); + expect(result.bar).to.equal('bar'); + }); + + it('should attempt to fetch the rootItemId, if rootItemId not provided', async () => { + nock(endpoint) + .post('/', /AppRootQuery/) + .reply(200, appRootQueryResponse); + + nock(endpoint) + .post('/', (body) => body.variables.rootItemId === 'GUIDGUIDGUID') + .reply(200, contentTokenQueryResponse); + + const service = new GraphQLContentTokenService({ + clientFactory, + siteName, + cacheEnabled: false, + }); + const result = await service.fetchContentTokens('en'); + expect(result).to.have.all.keys('foo', 'bar'); + // eslint-disable-next-line no-unused-expressions + expect(nock.isDone()).to.be.true; + }); + + it('should use a custom rootItemId, if provided', async () => { + const customRootId = 'cats'; + + nock(endpoint) + .post('/', (body) => body.variables.rootItemId === customRootId) + .reply(200, contentTokenQueryResponse); + + const service = new GraphQLContentTokenService({ + clientFactory, + siteName, + cacheEnabled: false, + rootItemId: customRootId, + }); + const result = await service.fetchContentTokens('en'); + expect(result).to.have.all.keys('foo', 'bar'); + }); + + it('should use a jssTemplateId, if provided', async () => { + const jssAppTemplateId = '{71d608ca-ac9c-4f1c-8e0a-85a6946e30f8}'; + const randomId = '{412286b7-6d4f-4deb-80e9-108ee986c6e9}'; + + nock(endpoint) + .post('/', (body) => body.variables.jssAppTemplateId === jssAppTemplateId) + .reply(200, { + data: { + layout: { + homePage: { + rootItem: [ + { + id: randomId, + }, + ], + }, + }, + }, + }); + + nock(endpoint) + .post('/', (body) => body.variables.rootItemId === randomId) + .reply(200, contentTokenQueryResponse); + + const service = new GraphQLContentTokenService({ + clientFactory, + siteName, + cacheEnabled: false, + jssAppTemplateId, + }); + + const result = await service.fetchContentTokens('en'); + expect(result).to.have.all.keys('foo', 'bar'); + }); + + it('should throw error if could not resolve rootItemId', async () => { + nock(endpoint) + .post('/', /AppRootQuery/) + .reply(200, { + data: { + layout: { + homePage: null, + }, + }, + }); + + const service = new GraphQLContentTokenService({ + clientFactory, + siteName, + cacheEnabled: false, + }); + + await service.fetchContentTokens('en').catch((error) => { + expect(error).to.be.instanceOf(Error); + expect(error.message).to.equal(queryError); + }); + }); + + it('should use default pageSize, if pageSize not provided', async () => { + nock(endpoint) + .post( + '/', + (body) => + body.query.indexOf('$pageSize: Int = 10') > 0 && body.variables.pageSize === undefined + ) + .reply(200, contentTokenQueryResponse); + + const service = new GraphQLContentTokenService({ + clientFactory, + siteName, + rootItemId, + cacheEnabled: false, + pageSize: undefined, + }); + const result = await service.fetchContentTokens('en'); + expect(result).to.have.all.keys('foo', 'bar'); + }); + + it('should use a custom pageSize, if provided', async () => { + const customPageSize = 2; + + nock(endpoint) + .post('/', (body) => body.variables.pageSize === customPageSize) + .reply(200, contentTokenQueryResponse); + + const service = new GraphQLContentTokenService({ + clientFactory, + siteName, + rootItemId, + cacheEnabled: false, + pageSize: customPageSize, + }); + const result = await service.fetchContentTokens('en'); + expect(result).to.have.all.keys('foo', 'bar'); + }); + + it('should use custom content token template ID, if provided', async () => { + const customTemplateId = 'custom-template-id'; + + nock(endpoint) + .post('/', (body) => body.variables.templates === customTemplateId) + .reply(200, contentTokenQueryResponse); + + const service = new GraphQLContentTokenService({ + clientFactory, + siteName, + rootItemId, + cacheEnabled: false, + contentTokenTemplateId: customTemplateId, + }); + const result = await service.fetchContentTokens('en'); + expect(result).to.have.all.keys('foo', 'bar'); + }); + + it('should use default content token template ID, if template ID not provided', async () => { + nock(endpoint) + .post('/', (body) => body.variables.templates === SitecoreTemplateId.ContentToken) + .reply(200, contentTokenQueryResponse); + + const service = new GraphQLContentTokenService({ + clientFactory, + siteName, + rootItemId, + cacheEnabled: false, + }); + const result = await service.fetchContentTokens('en'); + expect(result).to.have.all.keys('foo', 'bar'); + }); + + it('should use cache', async () => { + nock(endpoint, { reqheaders: { sc_apikey: apiKey } }) + .post('/', /ContentTokenSearch/gi) + .reply(200, contentTokenQueryResponse); + + const service = new GraphQLContentTokenService({ + clientFactory, + siteName, + rootItemId, + cacheEnabled: true, + cacheTimeout: 2, + }); + + const result1 = await service.fetchContentTokens('en'); + expect(result1).to.have.all.keys('foo', 'bar'); + + const result2 = await service.fetchContentTokens('en'); + expect(result2).to.have.all.keys('foo', 'bar'); + }); + + it('should provide a default GraphQL client', () => { + const service = new TestService({ + clientFactory, + siteName, + rootItemId, + cacheEnabled: false, + }); + + const graphQLClient = service.client as GraphQLClient; + const graphQLRequestClient = service.client as GraphQLRequestClient; + // eslint-disable-next-line no-unused-expressions + expect(graphQLClient).to.exist; + // eslint-disable-next-line no-unused-expressions + expect(graphQLRequestClient).to.exist; + }); + + it('should call clientFactory with the correct arguments', () => { + const clientFactorySpy: SinonSpy = sinon.spy(); + const mockServiceConfig = { + siteName: 'supersite', + clientFactory: clientFactorySpy, + retries: 3, + retryStrategy: { + getDelay: () => 1000, + shouldRetry: () => true, + }, + }; + + new GraphQLContentTokenService(mockServiceConfig); + + expect(clientFactorySpy.calledOnce).to.be.true; + + const calledWithArgs = clientFactorySpy.firstCall.args[0]; + expect(calledWithArgs.debugger).to.exist; + expect(calledWithArgs.retries).to.equal(mockServiceConfig.retries); + expect(calledWithArgs.retryStrategy).to.deep.equal(mockServiceConfig.retryStrategy); + }); +}); diff --git a/packages/sitecore-jss/src/i18n/graphql-content-token-service.ts b/packages/sitecore-jss/src/i18n/graphql-content-token-service.ts new file mode 100644 index 0000000000..bfd683a99a --- /dev/null +++ b/packages/sitecore-jss/src/i18n/graphql-content-token-service.ts @@ -0,0 +1,200 @@ +import { + GraphQLClient, + GraphQLRequestClientConfig, + GraphQLRequestClientFactory, +} from '../graphql-request-client'; +import { SitecoreTemplateId } from '../constants'; +import { ContentTokenPhrases, ContentTokenServiceBase } from './content-token-service'; +import { CacheOptions } from '../cache-client'; +import { getAppRootId, SearchQueryResult, SearchQueryVariables } from '../graphql'; +import debug from '../debug'; + +/** @private */ +export const queryError = + 'Valid value for rootItemId not provided and failed to auto-resolve app root item.'; + +const query = /* GraphQL */ ` + query ContentTokenSearch( + $rootItemId: String! + $language: String! + $templates: String! + $pageSize: Int = 10 + $after: String + ) { + search( + where: { + AND: [ + { name: "_path", value: $rootItemId, operator: CONTAINS } + { name: "_language", value: $language } + { name: "_templates", value: $templates, operator: CONTAINS } + ] + } + first: $pageSize + after: $after + ) { + total + pageInfo { + endCursor + hasNext + } + results { + key: field(name: "Key") { + value + } + value: field(name: "Value") { + value + } + } + } + } +`; + +/** + * Configuration options for @see GraphQLContentTokenService instances + */ +export interface GraphQLContentTokenServiceConfig + extends Omit, + CacheOptions, + Pick { + /** + * The name of the current Sitecore site. This is used to to determine the search query root + * in cases where one is not specified by the caller. + */ + siteName: string; + + /** + * A GraphQL Request Client Factory is a function that accepts configuration and returns an instance of a GraphQLRequestClient. + * This factory function is used to create and configure GraphQL clients for making GraphQL API requests. + */ + clientFactory: GraphQLRequestClientFactory; + + /** + * Optional. The template ID to use when searching for content token entries. + * @default '7d659ee9d4874d408a9210c6d68844c8' (/sitecore/templates/Feature/Experience Accelerator/Content Tokens/Content Token) + */ + contentTokenTemplateId?: string; + + /** + * Optional. The template ID of a JSS App to use when searching for the appRootId. + * @default '061cba1554744b918a0617903b102b82' (/sitecore/templates/Foundation/JavaScript Services/App) + */ + jssAppTemplateId?: string; +} + +/** + * The schema of data returned in response to a content token query request. + */ +export type ContentTokenQueryResult = { + key: { value: string }; + value: { value: string }; +}; + +/** + * Service that fetch content token data using Sitecore's GraphQL API. + * @augments ContentTokenServiceBase + */ +export class GraphQLContentTokenService extends ContentTokenServiceBase { + private graphQLClient: GraphQLClient; + + /** + * Creates an instance of graphQL content token service with the provided options + * @param {GraphQLContentTokenService} options instance + */ + constructor(public options: GraphQLContentTokenServiceConfig) { + super(options); + this.graphQLClient = this.getGraphQLClient(); + } + + /** + * Fetches content token data for internalization. Uses search query by default + * @param {string} language the language to fetch + * @returns {Promise} content token phrases + * @throws {Error} if the app root was not found for the specified site and language. + */ + async fetchContentTokens(language: string): Promise { + const cacheKey = this.options.siteName + language; + const cachedValue = this.getCacheValue(cacheKey); + if (cachedValue) { + debug.contenttokens( + 'using cached content token data for %s %s', + language, + this.options.siteName + ); + return cachedValue; + } + + const phrases = await this.fetchWithSearchQuery(language); + + this.setCacheValue(cacheKey, phrases); + return phrases; + } + + /** + * Fetches content token data with search query + * This is the default behavior for non-XMCloud deployments + * @param {string} language the language to fetch + * @default query (@see query) + * @returns {Promise} content token phrases + * @throws {Error} if the app root was not found for the specified site and language. + */ + async fetchWithSearchQuery(language: string): Promise { + debug.contenttokens('fetching site root for %s %s', language, this.options.siteName); + + // If the caller does not specify a root item ID, then we try to figure it out + const rootItemId = + this.options.rootItemId || + (await getAppRootId( + this.graphQLClient, + this.options.siteName, + language, + this.options.jssAppTemplateId + )); + + if (!rootItemId) { + throw new Error(queryError); + } + + debug.contenttokens('fetching content token data for %s %s', language, this.options.siteName); + const phrases: ContentTokenPhrases = {}; + let results: ContentTokenQueryResult[] = []; + let hasNext = true; + let after = ''; + + while (hasNext) { + const fetchResponse = await this.graphQLClient.request< + SearchQueryResult + >(query, { + rootItemId, + language, + templates: this.options.contentTokenTemplateId || SitecoreTemplateId.ContentToken, + pageSize: this.options.pageSize, + after, + }); + + results = results.concat(fetchResponse?.search?.results); + hasNext = fetchResponse.search.pageInfo.hasNext; + after = fetchResponse.search.pageInfo.endCursor; + } + + results.forEach((item) => (phrases[item.key.value] = item.value.value)); + + return phrases; + } + + /** + * Gets a GraphQL client that can make requests to the API. Uses graphql-request as the default + * library for fetching graphql data (@see GraphQLRequestClient). Override this method if you + * want to use something else. + * @returns {GraphQLClient} implementation + */ + protected getGraphQLClient(): GraphQLClient { + if (!this.options.clientFactory) { + throw new Error('clientFactory needs to be provided when initializing GraphQL client.'); + } + return this.options.clientFactory({ + debugger: debug.contenttokens, + retries: this.options.retries, + retryStrategy: this.options.retryStrategy, + }); + } +} diff --git a/packages/sitecore-jss/src/i18n/index.ts b/packages/sitecore-jss/src/i18n/index.ts index 2d6f9242cf..5534199182 100644 --- a/packages/sitecore-jss/src/i18n/index.ts +++ b/packages/sitecore-jss/src/i18n/index.ts @@ -1,3 +1,12 @@ +export { + ContentTokenPhrases, + ContentTokenService, + ContentTokenServiceBase, +} from './content-token-service'; +export { + GraphQLContentTokenServiceConfig, + GraphQLContentTokenService, +} from './graphql-content-token-service'; export { DictionaryPhrases, DictionaryService, DictionaryServiceBase } from './dictionary-service'; export { GraphQLDictionaryServiceConfig, diff --git a/packages/sitecore-jss/src/test-data/mockContentTokenQueryResponse.json b/packages/sitecore-jss/src/test-data/mockContentTokenQueryResponse.json new file mode 100644 index 0000000000..7c1bbf9fef --- /dev/null +++ b/packages/sitecore-jss/src/test-data/mockContentTokenQueryResponse.json @@ -0,0 +1,29 @@ +{ + "data": { + "search": { + "total": 5, + "pageInfo": { + "endCursor": "NQ==", + "hasNext": false + }, + "results": [ + { + "key": { + "value": "foo" + }, + "value": { + "value": "foo" + } + }, + { + "key": { + "value": "bar" + }, + "value": { + "value": "bar" + } + } + ] + } + } +} diff --git a/ref-docs/sitecore-jss/classes/i18n.ContentTokenServiceBase.md b/ref-docs/sitecore-jss/classes/i18n.ContentTokenServiceBase.md new file mode 100644 index 0000000000..9b6d95a9f3 --- /dev/null +++ b/ref-docs/sitecore-jss/classes/i18n.ContentTokenServiceBase.md @@ -0,0 +1,201 @@ +[@sitecore-jss/sitecore-jss](../README.md) / [i18n](../modules/i18n.md) / ContentTokenServiceBase + +# Class: ContentTokenServiceBase + +[i18n](../modules/i18n.md).ContentTokenServiceBase + +Base implementation of + +**`See`** + +ContentTokenService that handles caching content token values + +## Hierarchy + +- **`ContentTokenServiceBase`** + + ↳ [`GraphQLContentTokenService`](i18n.GraphQLContentTokenService.md) + +## Implements + +- [`ContentTokenService`](../interfaces/i18n.ContentTokenService.md) +- `CacheClient`\<[`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md)\> + +## Table of contents + +### Constructors + +- [constructor](i18n.ContentTokenServiceBase.md#constructor) + +### Properties + +- [cache](i18n.ContentTokenServiceBase.md#cache) +- [options](i18n.ContentTokenServiceBase.md#options) + +### Methods + +- [fetchContentTokens](i18n.ContentTokenServiceBase.md#fetchContentTokens) +- [getCacheClient](i18n.ContentTokenServiceBase.md#getcacheclient) +- [getCacheValue](i18n.ContentTokenServiceBase.md#getcachevalue) +- [setCacheValue](i18n.ContentTokenServiceBase.md#setcachevalue) + +## Constructors + +### constructor + +• **new ContentTokenServiceBase**(`options`) + +Initializes a new instance of + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `options` | `CacheOptions` | Configuration options | + +**`See`** + + - ContentTokenService using the provided + - CacheOptions + +#### Defined in + +[packages/sitecore-jss/src/i18n/content-token-service.ts:32](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/content-token-service.ts#L32) + +## Properties + +### cache + +• `Private` **cache**: `CacheClient`\<[`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md)\> + +#### Defined in + +[packages/sitecore-jss/src/i18n/content-token-service.ts:26](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/content-token-service.ts#L26) + +___ + +### options + +• **options**: `CacheOptions` + +Configuration options + +#### Defined in + +[packages/sitecore-jss/src/i18n/content-token-service.ts:32](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/content-token-service.ts#L32) + +## Methods + +### fetchContentTokens + +▸ `Abstract` **fetchContentTokens**(`language`): `Promise`\<[`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md)\> + +Fetch content tokens data for a language. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `language` | `string` | the language to be used to fetch the content tokens | + +#### Returns + +`Promise`\<[`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md)\> + +#### Implementation of + +[ContentTokenService](../interfaces/i18n.ContentTokenService.md).[fetchContentTokens](../interfaces/i18n.ContentTokenService.md#fetchContentTokens) + +#### Defined in + +[packages/sitecore-jss/src/i18n/content-token-service.ts:71](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/content-token-service.ts#L71) + +___ + +### getCacheClient + +▸ `Protected` **getCacheClient**(): `CacheClient`\<[`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md)\> + +Gets a cache client that can cache data. Uses memory-cache as the default +library for caching (@see MemoryCacheClient). Override this method if you +want to use something else. + +#### Returns + +`CacheClient`\<[`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md)\> + +implementation + +#### Defined in + +[packages/sitecore-jss/src/i18n/content-token-service.ts:62](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/content-token-service.ts#L62) + +___ + +### getCacheValue + +▸ **getCacheValue**(`key`): ``null`` \| [`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md) + +Retrieves a + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `key` | `string` | The cache key. | + +#### Returns + +``null`` \| [`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md) + +The + +**`See`** + + - ContentTokenPhrases value from the cache. + - ContentTokenPhrases value, or null if the specified key is not found in the cache. + +#### Implementation of + +CacheClient.getCacheValue + +#### Defined in + +[packages/sitecore-jss/src/i18n/content-token-service.ts:52](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/content-token-service.ts#L52) + +___ + +### setCacheValue + +▸ **setCacheValue**(`key`, `value`): [`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md) + +Caches a + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `key` | `string` | The cache key. | +| `value` | [`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md) | The value to cache. | + +#### Returns + +[`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md) + +The value added to the cache. + +**`See`** + +ContentTokenPhrases value for the specified cache key. + +**`Mixes`** + +CacheClient + +#### Implementation of + +CacheClient.setCacheValue + +#### Defined in + +[packages/sitecore-jss/src/i18n/content-token-service.ts:43](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/content-token-service.ts#L43) diff --git a/ref-docs/sitecore-jss/classes/i18n.GraphQLContentTokenService.md b/ref-docs/sitecore-jss/classes/i18n.GraphQLContentTokenService.md new file mode 100644 index 0000000000..f0a1d43e6e --- /dev/null +++ b/ref-docs/sitecore-jss/classes/i18n.GraphQLContentTokenService.md @@ -0,0 +1,262 @@ +[@sitecore-jss/sitecore-jss](../README.md) / [i18n](../modules/i18n.md) / GraphQLContentTokenService + +# Class: GraphQLContentTokenService + +[i18n](../modules/i18n.md).GraphQLContentTokenService + +Service that fetch content token data using Sitecore's GraphQL API. + +## Hierarchy + +- [`ContentTokenServiceBase`](i18n.ContentTokenServiceBase.md) + + ↳ **`GraphQLContentTokenService`** + +## Table of contents + +### Constructors + +- [constructor](i18n.GraphQLContentTokenService.md#constructor) + +### Properties + +- [graphQLClient](i18n.GraphQLContentTokenService.md#graphqlclient) +- [options](i18n.GraphQLContentTokenService.md#options) + +### Methods + +- [fetchContentTokens](i18n.GraphQLContentTokenService.md#fetchContentTokens) +- [fetchWithSearchQuery](i18n.GraphQLContentTokenService.md#fetchwithsearchquery) +- [getCacheClient](i18n.GraphQLContentTokenService.md#getcacheclient) +- [getCacheValue](i18n.GraphQLContentTokenService.md#getcachevalue) +- [getGraphQLClient](i18n.GraphQLContentTokenService.md#getgraphqlclient) +- [setCacheValue](i18n.GraphQLContentTokenService.md#setcachevalue) + +## Constructors + +### constructor + +• **new GraphQLContentTokenService**(`options`) + +Creates an instance of graphQL content token service with the provided options + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `options` | [`GraphQLContentTokenServiceConfig`](../interfaces/i18n.GraphQLContentTokenServiceConfig.md) | instance | + +#### Overrides + +[ContentTokenServiceBase](i18n.ContentTokenServiceBase.md).[constructor](i18n.ContentTokenServiceBase.md#constructor) + +#### Defined in + +[packages/sitecore-jss/src/i18n/graphql-content-token-service.ts:103](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/graphql-content-token-service.ts#L103) + +## Properties + +### graphQLClient + +• `Private` **graphQLClient**: [`GraphQLClient`](../interfaces/index.GraphQLClient.md) + +#### Defined in + +[packages/sitecore-jss/src/i18n/graphql-content-token-service.ts:97](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/graphql-content-token-service.ts#L97) + +___ + +### options + +• **options**: [`GraphQLContentTokenServiceConfig`](../interfaces/i18n.GraphQLContentTokenServiceConfig.md) + +instance + +#### Inherited from + +[ContentTokenServiceBase](i18n.ContentTokenServiceBase.md).[options](i18n.ContentTokenServiceBase.md#options) + +#### Defined in + +[packages/sitecore-jss/src/i18n/graphql-content-token-service.ts:103](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/graphql-content-token-service.ts#L103) + +## Methods + +### fetchContentTokens + +▸ **fetchContentTokens**(`language`): `Promise`\<[`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md)\> + +Fetches content token data for internalization. Uses search query by default + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `language` | `string` | the language to fetch | + +#### Returns + +`Promise`\<[`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md)\> + +content token phrases + +**`Throws`** + +if the app root was not found for the specified site and language. + +#### Overrides + +[ContentTokenServiceBase](i18n.ContentTokenServiceBase.md).[fetchContentTokens](i18n.ContentTokenServiceBase.md#fetchContentTokens) + +#### Defined in + +[packages/sitecore-jss/src/i18n/graphql-content-token-service.ts:114](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/graphql-content-token-service.ts#L114) + +___ + +### fetchWithSearchQuery + +▸ **fetchWithSearchQuery**(`language`): `Promise`\<[`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md)\> + +Fetches content token data with search query +This is the default behavior for non-XMCloud deployments + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `language` | `string` | the language to fetch | + +#### Returns + +`Promise`\<[`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md)\> + +content token phrases + +**`Default`** + +```ts +query (@see query) +``` + +**`Throws`** + +if the app root was not found for the specified site and language. + +#### Defined in + +[packages/sitecore-jss/src/i18n/graphql-content-token-service.ts:136](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/graphql-content-token-service.ts#L136) + +___ + +### getCacheClient + +▸ `Protected` **getCacheClient**(): `CacheClient`\<[`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md)\> + +Gets a cache client that can cache data. Uses memory-cache as the default +library for caching (@see MemoryCacheClient). Override this method if you +want to use something else. + +#### Returns + +`CacheClient`\<[`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md)\> + +implementation + +#### Inherited from + +[ContentTokenServiceBase](i18n.ContentTokenServiceBase.md).[getCacheClient](i18n.ContentTokenServiceBase.md#getcacheclient) + +#### Defined in + +[packages/sitecore-jss/src/i18n/content-token-service.ts:62](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/content-token-service.ts#L62) + +___ + +### getCacheValue + +▸ **getCacheValue**(`key`): ``null`` \| [`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md) + +Retrieves a + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `key` | `string` | The cache key. | + +#### Returns + +``null`` \| [`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md) + +The + +**`See`** + + - ContentTokenPhrases value from the cache. + - ContentTokenPhrases value, or null if the specified key is not found in the cache. + +#### Inherited from + +[ContentTokenServiceBase](i18n.ContentTokenServiceBase.md).[getCacheValue](i18n.ContentTokenServiceBase.md#getcachevalue) + +#### Defined in + +[packages/sitecore-jss/src/i18n/content-token-service.ts:52](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/content-token-service.ts#L52) + +___ + +### getGraphQLClient + +▸ `Protected` **getGraphQLClient**(): [`GraphQLClient`](../interfaces/index.GraphQLClient.md) + +Gets a GraphQL client that can make requests to the API. Uses graphql-request as the default +library for fetching graphql data (@see GraphQLRequestClient). Override this method if you +want to use something else. + +#### Returns + +[`GraphQLClient`](../interfaces/index.GraphQLClient.md) + +implementation + +#### Defined in + +[packages/sitecore-jss/src/i18n/graphql-content-token-service.ts:185](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/graphql-content-token-service.ts#L185) + +___ + +### setCacheValue + +▸ **setCacheValue**(`key`, `value`): [`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md) + +Caches a + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `key` | `string` | The cache key. | +| `value` | [`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md) | The value to cache. | + +#### Returns + +[`ContentTokenPhrases`](../interfaces/i18n.ContentTokenPhrases.md) + +The value added to the cache. + +**`See`** + +ContentTokenPhrases value for the specified cache key. + +**`Mixes`** + +CacheClient + +#### Inherited from + +[ContentTokenServiceBase](i18n.ContentTokenServiceBase.md).[setCacheValue](i18n.ContentTokenServiceBase.md#setcachevalue) + +#### Defined in + +[packages/sitecore-jss/src/i18n/content-token-service.ts:43](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/content-token-service.ts#L43) diff --git a/ref-docs/sitecore-jss/interfaces/i18n.ContentTokenPhrases.md b/ref-docs/sitecore-jss/interfaces/i18n.ContentTokenPhrases.md new file mode 100644 index 0000000000..7f9b87bf8d --- /dev/null +++ b/ref-docs/sitecore-jss/interfaces/i18n.ContentTokenPhrases.md @@ -0,0 +1,11 @@ +[@sitecore-jss/sitecore-jss](../README.md) / [i18n](../modules/i18n.md) / ContentTokenPhrases + +# Interface: ContentTokenPhrases + +[i18n](../modules/i18n.md).ContentTokenPhrases + +Object model for Sitecore content token phrases + +## Indexable + +▪ [k: `string`]: `string` \ No newline at end of file diff --git a/ref-docs/sitecore-jss/interfaces/i18n.ContentTokenService.md b/ref-docs/sitecore-jss/interfaces/i18n.ContentTokenService.md new file mode 100644 index 0000000000..01d24e91f6 --- /dev/null +++ b/ref-docs/sitecore-jss/interfaces/i18n.ContentTokenService.md @@ -0,0 +1,39 @@ +[@sitecore-jss/sitecore-jss](../README.md) / [i18n](../modules/i18n.md) / ContentTokenService + +# Interface: ContentTokenService + +[i18n](../modules/i18n.md).ContentTokenService + +Service that fetches content token data using Sitecore's GraphQL API. + +## Implemented by + +- [`ContentTokenServiceBase`](../classes/i18n.ContentTokenServiceBase.md) + +## Table of contents + +### Methods + +- [fetchContentTokens](i18n.ContentTokenService.md#fetchContentTokens) + +## Methods + +### fetchContentTokens + +▸ **fetchContentTokens**(`language`): `Promise`\<[`ContentTokenPhrases`](i18n.ContentTokenPhrases.md)\> + +Fetch content token data for a language. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `language` | `string` | the language to be used to fetch the content token | + +#### Returns + +`Promise`\<[`ContentTokenPhrases`](i18n.ContentTokenPhrases.md)\> + +#### Defined in + +[packages/sitecore-jss/src/i18n/content-token-service.ts:18](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/content-token-service.ts#L18) diff --git a/ref-docs/sitecore-jss/interfaces/i18n.GraphQLContentTokenServiceConfig.md b/ref-docs/sitecore-jss/interfaces/i18n.GraphQLContentTokenServiceConfig.md new file mode 100644 index 0000000000..2649d19340 --- /dev/null +++ b/ref-docs/sitecore-jss/interfaces/i18n.GraphQLContentTokenServiceConfig.md @@ -0,0 +1,218 @@ +[@sitecore-jss/sitecore-jss](../README.md) / [i18n](../modules/i18n.md) / GraphQLContentTokenServiceConfig + +# Interface: GraphQLContentTokenServiceConfig + +[i18n](../modules/i18n.md).GraphQLContentTokenServiceConfig + +Configuration options for + +**`See`** + +GraphQLContentTokenService instances + +## Hierarchy + +- `Omit`\<[`SearchQueryVariables`](graphql.SearchQueryVariables.md), ``"language"``\> + +- `CacheOptions` + +- `Pick`\<[`GraphQLRequestClientConfig`](../modules/index.md#graphqlrequestclientconfig), ``"retries"`` \| ``"retryStrategy"``\> + + ↳ **`GraphQLContentTokenServiceConfig`** + +## Table of contents + +### Properties + +- [cacheEnabled](i18n.GraphQLContentTokenServiceConfig.md#cacheenabled) +- [cacheTimeout](i18n.GraphQLContentTokenServiceConfig.md#cachetimeout) +- [clientFactory](i18n.GraphQLContentTokenServiceConfig.md#clientfactory) +- [contentTokenTemplateId](i18n.GraphQLContentTokenServiceConfig.md#contentTokenTemplateId) +- [jssAppTemplateId](i18n.GraphQLContentTokenServiceConfig.md#jssapptemplateid) +- [pageSize](i18n.GraphQLContentTokenServiceConfig.md#pagesize) +- [retries](i18n.GraphQLContentTokenServiceConfig.md#retries) +- [retryStrategy](i18n.GraphQLContentTokenServiceConfig.md#retrystrategy) +- [rootItemId](i18n.GraphQLContentTokenServiceConfig.md#rootitemid) +- [siteName](i18n.GraphQLContentTokenServiceConfig.md#sitename) +- [templates](i18n.GraphQLContentTokenServiceConfig.md#templates) + +## Properties + +### cacheEnabled + +• `Optional` **cacheEnabled**: `boolean` + +Enable/disable caching mechanism + +**`Default`** + +```ts +true +``` + +#### Inherited from + +CacheOptions.cacheEnabled + +#### Defined in + +[packages/sitecore-jss/src/cache-client.ts:40](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/cache-client.ts#L40) + +___ + +### cacheTimeout + +• `Optional` **cacheTimeout**: `number` + +Cache timeout (sec) + +**`Default`** + +```ts +60 +``` + +#### Inherited from + +CacheOptions.cacheTimeout + +#### Defined in + +[packages/sitecore-jss/src/cache-client.ts:45](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/cache-client.ts#L45) + +___ + +### clientFactory + +• **clientFactory**: [`GraphQLRequestClientFactory`](../modules/index.md#graphqlrequestclientfactory) + +A GraphQL Request Client Factory is a function that accepts configuration and returns an instance of a GraphQLRequestClient. +This factory function is used to create and configure GraphQL clients for making GraphQL API requests. + +#### Defined in + +[packages/sitecore-jss/src/i18n/graphql-content-token-service.ts:94](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/graphql-content-token-service.ts#L94) + +___ + +### contentTokenTemplateId + +• `Optional` **contentTokenTemplateId**: `string` + +Optional. The template ID to use when searching for dictionary entries. + +**`Default`** + +```ts +'7d659ee9d4874d408a9210c6d68844c8' (/sitecore/templates/Feature/Experience Accelerator/Content Tokens/Content Token) +``` + +#### Defined in + +[packages/sitecore-jss/src/i18n/graphql-content-token-service.ts:75](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/graphql-content-token-service.ts#L75) + +___ + +### jssAppTemplateId + +• `Optional` **jssAppTemplateId**: `string` + +Optional. The template ID of a JSS App to use when searching for the appRootId. + +**`Default`** + +```ts +'061cba1554744b918a0617903b102b82' (/sitecore/templates/Foundation/JavaScript Services/App) +``` + +#### Defined in + +[packages/sitecore-jss/src/i18n/graphql-content-token-service.ts:81](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/graphql-content-token-service.ts#L81) + +___ + +### pageSize + +• `Optional` **pageSize**: `number` + +common variable for all GraphQL queries +it will be used for every type of query to regulate result batch size +Optional. How many result items to fetch in each GraphQL call. This is needed for pagination. + +**`Default`** + +```ts +10 +``` + +#### Inherited from + +Omit.pageSize +___ + +### retries + +• `Optional` **retries**: `number` + +Number of retries for client. Will use the specified `retryStrategy`. + +#### Inherited from + +Pick.retries + +#### Defined in + +[packages/sitecore-jss/src/graphql-request-client.ts:83](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/graphql-request-client.ts#L83) + +___ + +### retryStrategy + +• `Optional` **retryStrategy**: [`RetryStrategy`](index.RetryStrategy.md) + +Retry strategy for the client. Uses `DefaultRetryStrategy` by default with exponential +back-off factor of 2 for codes 429, 502, 503, 504, 520, 521, 522, 523, 524. + +#### Inherited from + +Pick.retryStrategy + +#### Defined in + +[packages/sitecore-jss/src/graphql-request-client.ts:88](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/graphql-request-client.ts#L88) + +___ + +### rootItemId + +• `Optional` **rootItemId**: `string` + +Optional. The ID of the search root item. Fetch items that have this item as an ancestor. + +#### Inherited from + +Omit.rootItemId +___ + +### siteName + +• **siteName**: `string` + +The name of the current Sitecore site. This is used to to determine the search query root +in cases where one is not specified by the caller. + +#### Defined in + +[packages/sitecore-jss/src/i18n/graphql-content-token-service.ts:63](https://github.com/Sitecore/jss/blob/2794c8c94/packages/sitecore-jss/src/i18n/graphql-content-token-service.ts#L63) + +___ + +### templates + +• `Optional` **templates**: `string` + +Optional. Sitecore template ID(s). Fetch items that inherit from this template(s). + +#### Inherited from + +Omit.templates diff --git a/ref-docs/sitecore-jss/modules/i18n.md b/ref-docs/sitecore-jss/modules/i18n.md index 77d9f8dc9a..7d846c3ce0 100644 --- a/ref-docs/sitecore-jss/modules/i18n.md +++ b/ref-docs/sitecore-jss/modules/i18n.md @@ -6,14 +6,19 @@ ### Classes +- [ContentTokenServiceBase](../classes/i18n.ContentTokenServiceBase.md) - [DictionaryServiceBase](../classes/i18n.DictionaryServiceBase.md) +- [GraphQLContentTokenService](../classes/i18n.GraphQLContentTokenService.md) - [GraphQLDictionaryService](../classes/i18n.GraphQLDictionaryService.md) - [RestDictionaryService](../classes/i18n.RestDictionaryService.md) ### Interfaces +- [ContentTokenPhrases](../interfaces/i18n.ContentTokenPhrases.md) +- [ContentTokenService](../interfaces/i18n.ContentTokenService.md) - [DictionaryPhrases](../interfaces/i18n.DictionaryPhrases.md) - [DictionaryService](../interfaces/i18n.DictionaryService.md) +- [GraphQLContentTokenServiceConfig](../interfaces/i18n.GraphQLContentTokenServiceConfig.md) - [GraphQLDictionaryServiceConfig](../interfaces/i18n.GraphQLDictionaryServiceConfig.md) ### Type Aliases