diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ea8fa8f23..a48dc83688 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,13 @@ Our versioning strategy is as follows: ## Unreleased +### 🛠 Breaking Changes + +[sitecore-jss] Switch to edge site query for XP and gets config sites + sxa sites (ignoring website) + * Previously introduced Boolean `useSiteQuery` switch for XMCloud users has been removed. + * Search query usage has been removed. + * If you have any non-nextjs sites they should filter them out in multisite config plugin + ## 21.7.1 ### 🐛 Bug Fixes diff --git a/docs/upgrades/unreleased.md b/docs/upgrades/unreleased.md index 61009616db..5a330c4be2 100644 --- a/docs/upgrades/unreleased.md +++ b/docs/upgrades/unreleased.md @@ -8,6 +8,13 @@ ### nextjs-multisite +* **Update** packages\create-sitecore-jss\src\templates\nextjs-multisite\scripts\config\plugins\multisite.ts + +``` +//Remove +useSiteQuery: true, +``` + ### nextjs-styleguide ### react diff --git a/packages/create-sitecore-jss/src/templates/nextjs-multisite/scripts/config/plugins/multisite.ts b/packages/create-sitecore-jss/src/templates/nextjs-multisite/scripts/config/plugins/multisite.ts index 2e8e5e151f..56f7581f1c 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-multisite/scripts/config/plugins/multisite.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-multisite/scripts/config/plugins/multisite.ts @@ -18,10 +18,6 @@ class MultisitePlugin implements ConfigPlugin { try { const siteInfoService = new GraphQLSiteInfoService({ clientFactory: createGraphQLClientFactory(config), - <% if (templates.includes("nextjs-xmcloud")) { -%> - // enable site query for the service. Only works on XMCloud currently - useSiteQuery: true, - <% } -%> }); sites = await siteInfoService.fetchSiteInfo(); } catch (error) { diff --git a/packages/sitecore-jss/src/site/graphql-siteinfo-service.test.ts b/packages/sitecore-jss/src/site/graphql-siteinfo-service.test.ts index f36862680d..21ea34a13a 100644 --- a/packages/sitecore-jss/src/site/graphql-siteinfo-service.test.ts +++ b/packages/sitecore-jss/src/site/graphql-siteinfo-service.test.ts @@ -3,12 +3,8 @@ import { expect, spy, use } from 'chai'; import spies from 'chai-spies'; import nock from 'nock'; -import { - GraphQLSiteInfoService, - GraphQLSiteInfoResult, - GraphQLXmCloudSiteInfoResult, -} from './graphql-siteinfo-service'; -import { GraphQLRequestClient, PageInfo } from '../graphql'; +import { GraphQLSiteInfoService, GraphQLSiteInfoResult } from './graphql-siteinfo-service'; +import { GraphQLRequestClient } from '../graphql'; import debugApi from 'debug'; import debug from '../debug'; @@ -28,29 +24,24 @@ describe('GraphQLSiteInfoService', () => { hostName: string; language: string; }): GraphQLSiteInfoResult => ({ - name: { value: name }, - hostName: { value: hostName }, - language: { value: language }, + name: name, + hostName: hostName, + language: language, }); const nonEmptyResponse = ({ count = 1, - start = 0, - pageInfo = { hasNext: false, endCursor: '' }, sites = [], }: { count?: number; - start?: number; - pageInfo?: PageInfo; sites?: GraphQLSiteInfoResult[]; } = {}) => ({ data: { - search: { - pageInfo, - results: [ + site: { + siteInfoCollection: [ ...[...Array(count).keys()].map((n) => site({ - name: `site ${start + n}`, + name: `site ${n}`, hostName: 'restricted.gov', language: 'en', }) @@ -63,9 +54,8 @@ describe('GraphQLSiteInfoService', () => { const emptyResponse = { data: { - search: { - pageInfo: {}, - results: [], + site: { + siteInfoCollection: [], }, }, }; @@ -154,52 +144,6 @@ describe('GraphQLSiteInfoService', () => { }, ]); }); - - it('should return correct result using custom pageSize', async () => { - mockSiteInfoRequest(nonEmptyResponse({ count: 2, pageInfo: { hasNext: true, endCursor: '' } })); - mockSiteInfoRequest( - nonEmptyResponse({ count: 2, start: 2, pageInfo: { hasNext: true, endCursor: '' } }) - ); - mockSiteInfoRequest( - nonEmptyResponse({ count: 2, start: 4, pageInfo: { hasNext: false, endCursor: '' } }) - ); - - const service = new GraphQLSiteInfoService({ apiKey: apiKey, endpoint: endpoint, pageSize: 2 }); - const result = await service.fetchSiteInfo(); - expect(result).to.be.deep.equal([ - { - name: 'site 0', - hostName: 'restricted.gov', - language: 'en', - }, - { - name: 'site 1', - hostName: 'restricted.gov', - language: 'en', - }, - { - name: 'site 2', - hostName: 'restricted.gov', - language: 'en', - }, - { - name: 'site 3', - hostName: 'restricted.gov', - language: 'en', - }, - { - name: 'site 4', - hostName: 'restricted.gov', - language: 'en', - }, - { - name: 'site 5', - hostName: 'restricted.gov', - language: 'en', - }, - ]); - }); - it('should return empty array when empty result received', async () => { nock(endpoint) .post('/') @@ -271,146 +215,26 @@ describe('GraphQLSiteInfoService', () => { expect(nock.isDone(), 'skip request').to.be.false; }); - describe('Fetch with site query in XM Cloud', () => { - const site = ({ - name, - hostName, - language, - }: { - name: string; - hostName: string; - language: string; - }): GraphQLXmCloudSiteInfoResult => ({ - name, - hostName, - language, - }); - - const nonEmptyResponse = ({ - count = 1, - sites = [], - }: { - count?: number; - sites?: GraphQLXmCloudSiteInfoResult[]; - } = {}) => ({ - data: { - site: { - siteInfoCollection: [ - ...[...Array(count).keys()].map((n) => - site({ - name: `site ${n}`, - hostName: 'restricted.gov', - language: 'en', - }) - ), - ...sites, - ], - }, - }, - }); - - const emptyResponse = { - data: { - site: { - siteInfoCollection: [], - }, + it('should filter out default website', async () => { + mockSiteInfoRequest( + nonEmptyResponse({ + sites: [ + site({ + name: 'website', + hostName: 'notheadless.org', + language: '', + }), + ], + }) + ); + const service = new GraphQLSiteInfoService({ apiKey: apiKey, endpoint: endpoint }); + const result = await service.fetchSiteInfo(); + expect(result).to.be.deep.equal([ + { + name: 'site 0', + hostName: 'restricted.gov', + language: 'en', }, - }; - - const getSiteQuerySiteInfoService = (initProps: { [key: string]: unknown }) => { - return new GraphQLSiteInfoService({ useSiteQuery: true, ...initProps }); - }; - - it('should return correct result', async () => { - mockSiteInfoRequest( - nonEmptyResponse({ - sites: [ - site({ - name: 'public 0', - hostName: 'pr.showercurtains.org', - language: '', - }), - ], - }) - ); - const service = getSiteQuerySiteInfoService({ apiKey: apiKey, endpoint: endpoint }); - const result = await service.fetchSiteInfo(); - expect(result).to.be.deep.equal([ - { - name: 'site 0', - hostName: 'restricted.gov', - language: 'en', - }, - { - name: 'public 0', - hostName: 'pr.showercurtains.org', - language: '', - }, - ]); - }); - - it('should return correct result using clientFactory', async () => { - mockSiteInfoRequest( - nonEmptyResponse({ - sites: [ - site({ - name: 'public 0', - hostName: 'pr.showercurtains.org', - language: '', - }), - ], - }) - ); - const clientFactory = GraphQLRequestClient.createClientFactory({ - endpoint, - apiKey, - }); - const service = getSiteQuerySiteInfoService({ clientFactory }); - const result = await service.fetchSiteInfo(); - expect(result).to.be.deep.equal([ - { - name: 'site 0', - hostName: 'restricted.gov', - language: 'en', - }, - { - name: 'public 0', - hostName: 'pr.showercurtains.org', - language: '', - }, - ]); - }); - - it('should return empty array when empty result received', async () => { - nock(endpoint) - .post('/') - .reply(200, emptyResponse); - const service = getSiteQuerySiteInfoService({ apiKey: apiKey, endpoint: endpoint }); - const result = await service.fetchSiteInfo(); - expect(result).to.deep.equal([]); - }); - - it('should filter out default website', async () => { - mockSiteInfoRequest( - nonEmptyResponse({ - sites: [ - site({ - name: 'website', - hostName: 'notheadless.org', - language: '', - }), - ], - }) - ); - const service = getSiteQuerySiteInfoService({ apiKey: apiKey, endpoint: endpoint }); - const result = await service.fetchSiteInfo(); - expect(result).to.be.deep.equal([ - { - name: 'site 0', - hostName: 'restricted.gov', - language: 'en', - }, - ]); - }); + ]); }); }); diff --git a/packages/sitecore-jss/src/site/graphql-siteinfo-service.ts b/packages/sitecore-jss/src/site/graphql-siteinfo-service.ts index e7d7fe661b..2093777d0f 100644 --- a/packages/sitecore-jss/src/site/graphql-siteinfo-service.ts +++ b/packages/sitecore-jss/src/site/graphql-siteinfo-service.ts @@ -1,44 +1,8 @@ -import { GraphQLClient, GraphQLRequestClient, PageInfo } from '../graphql'; +import { GraphQLClient, GraphQLRequestClient } from '../graphql'; import debug from '../debug'; import { CacheClient, CacheOptions, MemoryCacheClient } from '../cache-client'; import { GraphQLRequestClientFactory } from '../graphql-request-client'; -const headlessSiteGroupingTemplate = 'E46F3AF2-39FA-4866-A157-7017C4B2A40C'; -const sitecoreContentRootItem = '0DE95AE4-41AB-4D01-9EB0-67441B7C2450'; - -const defaultQuery = /* GraphQL */ ` - query($pageSize: Int = 10, $after: String) { - search( - where: { - AND: [ - { name: "_templates", value: "${headlessSiteGroupingTemplate}", operator: CONTAINS } - { name: "_path", value: "${sitecoreContentRootItem}", operator: CONTAINS } - ] - } - first: $pageSize - after: $after - ) { - pageInfo { - endCursor - hasNext - } - results { - ... on Item { - name: field(name: "SiteName") { - value - } - hostName: field(name: "Hostname") { - value - } - language: field(name: "Language") { - value - } - } - } - } - } -`; - const siteQuery = /* GraphQL */ ` query { site { @@ -92,38 +56,15 @@ export type GraphQLSiteInfoServiceConfig = CacheOptions & { * This factory function is used to create and configure GraphQL clients for making GraphQL API requests. */ clientFactory?: GraphQLRequestClientFactory; - /** - * Boolean indicating if service will use site GQL query instead of search - */ - useSiteQuery?: boolean; }; type GraphQLSiteInfoResponse = { - search: { - pageInfo: PageInfo; - results: GraphQLSiteInfoResult[]; - }; -}; - -export type GraphQLSiteInfoResult = { - name: { - value: string; - }; - hostName: { - value: string; - }; - language: { - value: string; - }; -}; - -type GraphQLXmCloudSiteInfoResponse = { site: { - siteInfoCollection: GraphQLXmCloudSiteInfoResult[]; + siteInfoCollection: GraphQLSiteInfoResult[]; }; }; -export type GraphQLXmCloudSiteInfoResult = { +export type GraphQLSiteInfoResult = { name: string; hostName: string; language: string; @@ -133,10 +74,6 @@ export class GraphQLSiteInfoService { private graphQLClient: GraphQLClient; private cache: CacheClient; - protected get query(): string { - return defaultQuery; - } - /** * site query is available on XM Cloud and XP 10.4+ */ @@ -163,44 +100,14 @@ export class GraphQLSiteInfoService { return []; } - const results: SiteInfo[] = this.config.useSiteQuery - ? await this.fetchWithSiteQuery() - : await this.fetchWithDefaultQuery(); + const results: SiteInfo[] = await this.fetchWithSiteQuery(); this.cache.setCacheValue(this.getCacheKey(), results); return results; } - protected async fetchWithDefaultQuery(): Promise { - const results: SiteInfo[] = []; - let hasNext = true; - let after = ''; - - while (hasNext) { - const response = await this.graphQLClient.request(this.query, { - pageSize: this.config.pageSize, - after, - }); - const result = response?.search?.results?.reduce((result, current) => { - result.push({ - name: current.name.value, - hostName: current.hostName.value, - language: current.language.value, - }); - return result; - }, []); - - results.push(...result); - hasNext = response.search.pageInfo.hasNext; - after = response.search.pageInfo.endCursor; - } - return results; - } - protected async fetchWithSiteQuery(): Promise { - const response = await this.graphQLClient.request( - this.siteQuery - ); + const response = await this.graphQLClient.request(this.siteQuery); const result = response?.site?.siteInfoCollection?.reduce((result, current) => { // filter out built in website current.name !== 'website' &&