From 31adef977ae4f275eaf27ecbdc282273b8ac6ece Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Thu, 2 Nov 2023 12:18:05 +0200 Subject: [PATCH 01/37] Initial commit --- .../src/templates/nextjs-personalize/.env | 10 -- .../src/lib/middleware/plugins/personalize.ts | 6 +- packages/sitecore-jss-nextjs/package.json | 5 +- packages/sitecore-jss-nextjs/src/index.ts | 1 - .../middleware/personalize-middleware.test.ts | 168 +++++++++++------- .../src/middleware/personalize-middleware.ts | 114 ++++++------ .../sitecore-jss/src/personalize/index.ts | 2 +- .../src/personalize/pos-resolver.test.ts | 77 -------- .../src/personalize/pos-resolver.ts | 9 - .../src/site/graphql-siteinfo-service.test.ts | 37 ---- .../src/site/graphql-siteinfo-service.ts | 14 -- 11 files changed, 166 insertions(+), 277 deletions(-) delete mode 100644 packages/sitecore-jss/src/personalize/pos-resolver.test.ts delete mode 100644 packages/sitecore-jss/src/personalize/pos-resolver.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/.env b/packages/create-sitecore-jss/src/templates/nextjs-personalize/.env index a508d9edfb..2263be5faa 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/.env +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/.env @@ -1,18 +1,8 @@ -# Your Sitecore CDP API target (specific to your data center region) -NEXT_PUBLIC_CDP_TARGET_URL= - -# Your Sitecore CDP client key -NEXT_PUBLIC_CDP_CLIENT_KEY= - # An optional Sitecore Personalize scope identifier. # This can be used to isolate personalization data when multiple XM Cloud Environments share a Personalize tenant. # This should match the PAGES_PERSONALIZE_SCOPE environment variable for your connected XM Cloud Environment. NEXT_PUBLIC_PERSONALIZE_SCOPE= -# Your Sitecore CDP point(s) of sale -# Can be provided as a single value (mypoint.com) or a multi-value JSON with locales ({"en":"en.mypoint.com","fr":"fr.mypoint.com"} etc) -NEXT_PUBLIC_CDP_POINTOFSALE= - # Timeout (ms) for Sitecore CDP requests to respond within. Default is 400. PERSONALIZE_MIDDLEWARE_CDP_TIMEOUT= diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/middleware/plugins/personalize.ts b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/middleware/plugins/personalize.ts index 44387365a1..6e48968b43 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/middleware/plugins/personalize.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/middleware/plugins/personalize.ts @@ -33,8 +33,7 @@ class PersonalizePlugin implements MiddlewarePlugin { }, // Configuration for your Sitecore CDP endpoint cdpConfig: { - endpoint: process.env.NEXT_PUBLIC_CDP_TARGET_URL || '', - clientKey: process.env.NEXT_PUBLIC_CDP_CLIENT_KEY || '', + sitecoreContextId: config.sitecoreEdgeContextId, timeout: (process.env.PERSONALIZE_MIDDLEWARE_CDP_TIMEOUT && parseInt(process.env.PERSONALIZE_MIDDLEWARE_CDP_TIMEOUT)) || @@ -50,9 +49,6 @@ class PersonalizePlugin implements MiddlewarePlugin { excludeRoute: () => false, // Site resolver implementation siteResolver, - // Personalize middleware will use PosResolver.resolve(site, language) (same as CdpPageView) by default to get point of sale. - // You can also pass a custom point of sale resolver into middleware to override it like so: - // getPointOfSale: (site, language) => { ... } }); } diff --git a/packages/sitecore-jss-nextjs/package.json b/packages/sitecore-jss-nextjs/package.json index 13df06aa87..3a6b9d1842 100644 --- a/packages/sitecore-jss-nextjs/package.json +++ b/packages/sitecore-jss-nextjs/package.json @@ -30,7 +30,7 @@ "url": "https://github.com/sitecore/jss/issues" }, "devDependencies": { - "@sitecore/engage": "^1.4.1", + "@sitecore-cloudsdk/personalize": "^0.1.0-rc.0", "@types/chai": "^4.3.4", "@types/chai-as-promised": "^7.1.5", "@types/chai-string": "^1.4.2", @@ -66,7 +66,8 @@ "typescript": "~4.9.4" }, "peerDependencies": { - "@sitecore/engage": "^1.4.1", + "@sitecore-cloudsdk/events": "^0.1.0-rc.0", + "@sitecore-cloudsdk/personalize": "^0.1.0-rc.0", "next": "^13.4.16", "react": "^18.2.0", "react-dom": "^18.2.0" diff --git a/packages/sitecore-jss-nextjs/src/index.ts b/packages/sitecore-jss-nextjs/src/index.ts index e753bbee5d..fc1f104283 100644 --- a/packages/sitecore-jss-nextjs/src/index.ts +++ b/packages/sitecore-jss-nextjs/src/index.ts @@ -97,7 +97,6 @@ export { getPersonalizedRewriteData, normalizePersonalizedRewrite, CdpHelper, - PosResolver, } from '@sitecore-jss/sitecore-jss/personalize'; export { diff --git a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.test.ts b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.test.ts index 20a9e6a1b1..54a8ab35a6 100644 --- a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.test.ts +++ b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.test.ts @@ -7,8 +7,8 @@ import sinon, { spy } from 'sinon'; import nextjs, { NextRequest, NextResponse } from 'next/server'; import { debug } from '@sitecore-jss/sitecore-jss'; import { SiteResolver } from '@sitecore-jss/sitecore-jss/site'; -import { PersonalizeMiddleware } from './personalize-middleware'; import { ExperienceParams } from '@sitecore-jss/sitecore-jss/personalize'; +import { PersonalizeMiddleware } from './personalize-middleware'; use(sinonChai); const expect = chai.use(chaiString).expect; @@ -202,24 +202,32 @@ describe('PersonalizeMiddleware', () => { edgeConfig, }); - const engageServer = (middleware['initializeEngageServer'] = sinon.stub().returns({ - handleCookie: async () => { - return props.handleCookieStub || Promise.resolve(); - }, - event: async () => { - return Promise.resolve(null); - }, - personalize: async () => { - return Promise.resolve({ variantId: props.variantId }); - }, - identity: async () => { - return Promise.resolve(null); - }, - pageView: async () => { - return Promise.resolve(null); - }, - version: '1.0', - })); + const initPersonalizeServer = (middleware['initPersonalizeServer'] = sinon.stub()); + + const personalize = (middleware['personalize'] = sinon.stub().returns( + Promise.resolve({ + variantId: props.variantId, + }) + )); + + // { + // handleCookie: async () => { + // return props.handleCookieStub || Promise.resolve(); + // }, + // event: async () => { + // return Promise.resolve(null); + // }, + // personalize: async () => { + // return Promise.resolve({ variantId: props.variantId }); + // }, + // identity: async () => { + // return Promise.resolve(null); + // }, + // pageView: async () => { + // return Promise.resolve(null); + // }, + // version: '1.0', + // } const getPersonalizeInfo = (middleware['personalizeService']['getPersonalizeInfo'] = props.getPersonalizeInfoStub || @@ -238,7 +246,8 @@ describe('PersonalizeMiddleware', () => { middleware, getPersonalizeInfo, siteResolver, - engageServer, + initPersonalizeServer, + personalize, }; }; @@ -439,7 +448,7 @@ describe('PersonalizeMiddleware', () => { it('no variant identified', async () => { const req = createRequest(); const res = createResponse(); - const { middleware, getPersonalizeInfo, engageServer } = createMiddleware({ + const { middleware, getPersonalizeInfo, initPersonalizeServer, personalize } = createMiddleware({ variantId: undefined, }); const headers = {}; @@ -452,7 +461,8 @@ describe('PersonalizeMiddleware', () => { headers, }); expect(getPersonalizeInfo.calledWith('/styleguide', 'en')).to.be.true; - expect(engageServer.called).to.be.true; + expect(initPersonalizeServer.called).to.be.true; + expect(personalize.called).to.be.true; validateDebugLog('skipped (no variant identified)'); expect(finalRes).to.deep.equal(res); }); @@ -460,10 +470,11 @@ describe('PersonalizeMiddleware', () => { const req = createRequest(); const res = createResponse(); const handleCookieStub = sinon.stub().resolves(); - const { middleware, getPersonalizeInfo, engageServer } = createMiddleware({ - variantId: 'invalid-variant', - handleCookieStub, - }); + const { middleware, getPersonalizeInfo, initPersonalizeServer, personalize } = + createMiddleware({ + variantId: 'invalid-variant', + handleCookieStub, + }); const finalRes = await middleware.getHandler()(req, res); const headers = {}; req.headers.forEach((value, key) => (headers[key] = value)); @@ -474,7 +485,8 @@ describe('PersonalizeMiddleware', () => { headers, }); expect(getPersonalizeInfo.calledWith('/styleguide', 'en')).to.be.true; - expect(engageServer.called).to.be.true; + expect(initPersonalizeServer.called).to.be.true; + expect(personalize.called).to.be.true; validateDebugLog('skipped (invalid variant)'); expect(finalRes).to.deep.equal(res); }); @@ -492,14 +504,15 @@ describe('PersonalizeMiddleware', () => { }); const res = createResponse(); const nextRewriteStub = sinon.stub(nextjs.NextResponse, 'rewrite').returns(res); - const { middleware, getPersonalizeInfo, siteResolver, engageServer } = createMiddleware({ - language, - variantId: 'variant-2', - personalizeInfo: { - variantIds, - contentId, - }, - }); + const { middleware, getPersonalizeInfo, siteResolver, initPersonalizeServer, personalize } = + createMiddleware({ + language, + variantId: 'variant-2', + personalizeInfo: { + variantIds, + contentId, + }, + }); const finalRes = await middleware.getHandler()(req, res); validateDebugLog('personalize middleware start: %o', { @@ -511,7 +524,8 @@ describe('PersonalizeMiddleware', () => { language: language, }); expect(getPersonalizeInfo.calledWith('/styleguide', 'da-DK')).to.be.true; - expect(engageServer.calledOnce).to.be.true; + expect(initPersonalizeServer.calledOnce).to.be.true; + expect(personalize.calledOnce).to.be.true; validateEndMessageDebugLog('personalize middleware end in %dms: %o', { rewritePath: '/_variantId_variant-2/styleguide', headers: { @@ -534,9 +548,10 @@ describe('PersonalizeMiddleware', () => { }); const res = createResponse(); const nextRewriteStub = sinon.stub(nextjs.NextResponse, 'rewrite').returns(res); - const { middleware, getPersonalizeInfo, siteResolver, engageServer } = createMiddleware({ - variantId: 'variant-2', - }); + const { middleware, getPersonalizeInfo, siteResolver, initPersonalizeServer, personalize } = + createMiddleware({ + variantId: 'variant-2', + }); const finalRes = await middleware.getHandler()(req, res); validateDebugLog('personalize middleware start: %o', { @@ -548,7 +563,8 @@ describe('PersonalizeMiddleware', () => { language: 'en', }); expect(getPersonalizeInfo.calledWith('/styleguide', 'en')).to.be.true; - expect(engageServer.calledOnce).to.be.true; + expect(initPersonalizeServer.calledOnce).to.be.true; + expect(personalize.calledOnce).to.be.true; validateEndMessageDebugLog('personalize middleware end in %dms: %o', { rewritePath: '/_variantId_variant-2/styleguide', headers: { @@ -566,13 +582,15 @@ describe('PersonalizeMiddleware', () => { const req = createRequest(); const res = createResponse(); const nextRewriteStub = sinon.stub(nextjs.NextResponse, 'rewrite').returns(res); - const { middleware, getPersonalizeInfo, siteResolver, engageServer } = createMiddleware({ - variantId: 'variant-2', - }); + const { middleware, getPersonalizeInfo, siteResolver, initPersonalizeServer, personalize } = + createMiddleware({ + variantId: 'variant-2', + }); const finalRes = await middleware.getHandler()(req); expect(getPersonalizeInfo.calledWith('/styleguide', 'en')).to.be.true; - expect(engageServer.calledOnce).to.be.true; + expect(initPersonalizeServer.calledOnce).to.be.true; + expect(personalize.calledOnce).to.be.true; validateDebugLog('personalize middleware start: %o', { headers: { ...req.headers, @@ -599,9 +617,10 @@ describe('PersonalizeMiddleware', () => { const req = createRequest({ headerValues: { referer: null } }); const res = createResponse(); const nextRewriteStub = sinon.stub(nextjs.NextResponse, 'rewrite').returns(res); - const { middleware, getPersonalizeInfo, siteResolver, engageServer } = createMiddleware({ - variantId: 'variant-2', - }); + const { middleware, getPersonalizeInfo, siteResolver, initPersonalizeServer, personalize } = + createMiddleware({ + variantId: 'variant-2', + }); const finalRes = await middleware.getHandler()(req, res); validateDebugLog('personalize middleware start: %o', { @@ -613,7 +632,8 @@ describe('PersonalizeMiddleware', () => { language: 'en', }); expect(getPersonalizeInfo.calledWith('/styleguide', 'en')).to.be.true; - expect(engageServer.calledOnce).to.be.true; + expect(initPersonalizeServer.calledOnce).to.be.true; + expect(personalize.calledOnce).to.be.true; validateEndMessageDebugLog('personalize middleware end in %dms: %o', { rewritePath: '/_variantId_variant-2/styleguide', headers: { @@ -635,9 +655,10 @@ describe('PersonalizeMiddleware', () => { }, }); const nextRewriteStub = sinon.stub(nextjs.NextResponse, 'rewrite').returns(res); - const { middleware, getPersonalizeInfo, engageServer, siteResolver } = createMiddleware({ - variantId: 'variant-2', - }); + const { middleware, getPersonalizeInfo, initPersonalizeServer, personalize, siteResolver } = + createMiddleware({ + variantId: 'variant-2', + }); const finalRes = await middleware.getHandler()(req, res); validateDebugLog('personalize middleware start: %o', { @@ -649,7 +670,8 @@ describe('PersonalizeMiddleware', () => { language: 'en', }); expect(getPersonalizeInfo.calledWith('/styleguide', 'en', 'foo')).to.be.true; - expect(engageServer.calledOnce).to.be.true; + expect(initPersonalizeServer.calledOnce).to.be.true; + expect(personalize.calledOnce).to.be.true; validateEndMessageDebugLog('personalize middleware end in %dms: %o', { rewritePath: '/_variantId_variant-2/styleguide', headers: { @@ -672,9 +694,10 @@ describe('PersonalizeMiddleware', () => { }, }); const nextRewriteStub = sinon.stub(nextjs.NextResponse, 'rewrite').returns(res); - const { middleware, getPersonalizeInfo, engageServer, siteResolver } = createMiddleware({ - variantId: 'variant-2', - }); + const { middleware, getPersonalizeInfo, initPersonalizeServer, personalize, siteResolver } = + createMiddleware({ + variantId: 'variant-2', + }); const finalRes = await middleware.getHandler()(req, res); validateDebugLog('personalize middleware start: %o', { @@ -686,7 +709,8 @@ describe('PersonalizeMiddleware', () => { language: 'en', }); expect(getPersonalizeInfo.calledWith('/styleguide', 'en', siteName)).to.be.true; - expect(engageServer.calledOnce).to.be.true; + expect(initPersonalizeServer.calledOnce).to.be.true; + expect(personalize.calledOnce).to.be.true; validateEndMessageDebugLog('personalize middleware end in %dms: %o', { rewritePath: '/_variantId_variant-2/_site_nextjs-app/styleguide', headers: { @@ -708,9 +732,10 @@ describe('PersonalizeMiddleware', () => { }); const res = createResponse(); const nextRewriteStub = sinon.stub(nextjs.NextResponse, 'rewrite').returns(res); - const { middleware, getPersonalizeInfo, engageServer, siteResolver } = createMiddleware({ - variantId: 'variant-2', - }); + const { middleware, getPersonalizeInfo, initPersonalizeServer, personalize, siteResolver } = + createMiddleware({ + variantId: 'variant-2', + }); const finalRes = await middleware.getHandler()(req, res); validateDebugLog('personalize middleware start: %o', { @@ -722,7 +747,8 @@ describe('PersonalizeMiddleware', () => { language: 'en', }); expect(getPersonalizeInfo.calledWith('/styleguide', 'en', siteName)).to.be.true; - expect(engageServer.calledOnce).to.be.true; + expect(initPersonalizeServer.calledOnce).to.be.true; + expect(personalize.calledOnce).to.be.true; validateEndMessageDebugLog('personalize middleware end in %dms: %o', { rewritePath: '/_variantId_variant-2/styleguide', headers: { @@ -744,12 +770,14 @@ describe('PersonalizeMiddleware', () => { }); const res = createResponse(); const nextRewriteStub = sinon.stub(nextjs.NextResponse, 'rewrite').returns(res); - const { middleware, getPersonalizeInfo, engageServer, siteResolver } = createMiddleware({ - variantId: 'variant-2', - defaultHostname: 'foobar', - }); + const { middleware, getPersonalizeInfo, initPersonalizeServer, personalize, siteResolver } = + createMiddleware({ + variantId: 'variant-2', + defaultHostname: 'foobar', + }); const finalRes = await middleware.getHandler()(req, res); - expect(engageServer.calledOnce).to.be.true; + expect(initPersonalizeServer.calledOnce).to.be.true; + expect(personalize.calledOnce).to.be.true; validateDebugLog('personalize middleware start: %o', { headers: { ...req.headers }, hostname: 'foobar', @@ -796,13 +824,15 @@ describe('PersonalizeMiddleware', () => { const getPersonalizeInfoWithError = sinon.stub().throws(error); - const { middleware, getPersonalizeInfo, engageServer } = createMiddleware({ - getPersonalizeInfoStub: getPersonalizeInfoWithError, - }); + const { middleware, getPersonalizeInfo, initPersonalizeServer, personalize } = + createMiddleware({ + getPersonalizeInfoStub: getPersonalizeInfoWithError, + }); const finalRes = await middleware.getHandler()(req, res); - expect(engageServer.called).to.be.false; + expect(initPersonalizeServer.called).to.be.false; + expect(personalize.called).to.be.false; expect(getPersonalizeInfo.called).to.be.true; expect(errorSpy.getCall(0).calledWith('Personalize middleware failed:')).to.be.true; diff --git a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts index 03601daab8..0ff1e588e0 100644 --- a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts +++ b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts @@ -3,22 +3,19 @@ import { GraphQLPersonalizeService, GraphQLPersonalizeServiceConfig, getPersonalizedRewrite, - PosResolver, + PersonalizeInfo, } from '@sitecore-jss/sitecore-jss/personalize'; -import { SiteInfo } from '@sitecore-jss/sitecore-jss/site'; import { debug } from '@sitecore-jss/sitecore-jss'; import { MiddlewareBase, MiddlewareBaseConfig } from './middleware'; -import { initServer, EngageServer } from '@sitecore/engage'; +import { initServer, personalizeServer } from '@sitecore-cloudsdk/personalize'; +import { init, initServer } from '@sitecore-cloudsdk/events'; + export type CdpServiceConfig = { /** - * Your Sitecore CDP API endpoint - */ - endpoint: string; - /** - * The client key to use for authentication + * Your unified Sitecore Edge Context Id */ - clientKey: string; + sitecoreEdgeContextId: string; /** * The Sitecore CDP channel to use for events. Uses 'WEB' by default. */ @@ -42,13 +39,6 @@ export type PersonalizeMiddlewareConfig = MiddlewareBaseConfig & { * Configuration for your Sitecore CDP endpoint */ cdpConfig: CdpServiceConfig; - /** - * function to resolve point of sale for a site - * @param {Siteinfo} site to get info from - * @param {string} language to get info for - * @returns point of sale value for site/language combination - */ - getPointOfSale?: (site: SiteInfo, language: string) => string; }; /** @@ -101,22 +91,53 @@ export class PersonalizeMiddleware extends MiddlewareBase { }; } - protected initializeEngageServer( - hostName: string, - site: SiteInfo, - language: string - ): EngageServer { - const engageServer = initServer({ - clientKey: this.config.cdpConfig.clientKey, - targetURL: this.config.cdpConfig.endpoint, - pointOfSale: this.config.getPointOfSale - ? this.config.getPointOfSale(site, language) - : PosResolver.resolve(site, language), - cookieDomain: hostName, - forceServerCookieMode: true, - }); + protected async initPersonalizeServer({ + hostname, + sitecoreEdgeContextId, + siteName, + request, + response, + }: { + hostname: string; + sitecoreEdgeContextId: string; + siteName: string; + request: NextRequest; + response: NextResponse; + }): Promise { + await initServer( + { + sitecoreEdgeContextId, + siteName, + cookieDomain: hostname, + enableServerCookie: true, + }, + request, + response + ); + } + + protected async personalize({ + params, + personalizeInfo, + language, + timeout, + }: { + personalizeInfo: PersonalizeInfo; + params: ExperienceParams, + language: string; + timeout?: number; + }, request: NextRequest) { + const personalizationData = { + channel: this.config.cdpConfig.channel || 'WEB', + currency: this.config.cdpConfig.currency ?? 'USA', + friendlyId: personalizeInfo.contentId, + params, + language, + }; - return engageServer; + return (await personalizeServer(personalizationData, request, timeout)) as { + variantId: string; + }; } protected getExperienceParams(req: NextRequest): ExperienceParams { @@ -194,36 +215,25 @@ export class PersonalizeMiddleware extends MiddlewareBase { return response; } - const engageServer = this.initializeEngageServer(hostname, site, language); + await this.initPersonalizeServer({ + hostname, + siteName: site.name, + sitecoreEdgeContextId: this.config.cdpConfig.sitecoreEdgeContextId, + request: req, + response, + }); - // creates the browser ID cookie on the server side - // and includes the cookie in the response header - try { - await engageServer.handleCookie(req, response, timeout); - } catch (error) { - debug.personalize('skipped (browser id generation failed)'); - throw error; - } const params = this.getExperienceParams(req); debug.personalize('executing experience for %s %s %o', personalizeInfo.contentId, params); - const personalizationData = { - channel: this.config.cdpConfig.channel || 'WEB', - currency: this.config.cdpConfig.currency ?? 'USA', - friendlyId: personalizeInfo.contentId, - params, - language, - }; - let variantId; - // Execute targeted experience in CDP + // Execute targeted experience in Personalize SDK // eslint-disable-next-line no-useless-catch try { - variantId = ((await engageServer.personalize(personalizationData, req, timeout)) as { - variantId: string; - }).variantId; + const personalization = await this.personalize({ personalizeInfo, params, language, timeout }, req); + variantId = personalization.variantId; } catch (error) { throw error; } diff --git a/packages/sitecore-jss/src/personalize/index.ts b/packages/sitecore-jss/src/personalize/index.ts index 55363c3c2a..7f51a7fbf2 100644 --- a/packages/sitecore-jss/src/personalize/index.ts +++ b/packages/sitecore-jss/src/personalize/index.ts @@ -1,6 +1,6 @@ export { personalizeLayout } from './layout-personalizer'; -export { PosResolver } from './pos-resolver'; export { + PersonalizeInfo, GraphQLPersonalizeService, GraphQLPersonalizeServiceConfig, } from './graphql-personalize-service'; diff --git a/packages/sitecore-jss/src/personalize/pos-resolver.test.ts b/packages/sitecore-jss/src/personalize/pos-resolver.test.ts deleted file mode 100644 index 25cea8c230..0000000000 --- a/packages/sitecore-jss/src/personalize/pos-resolver.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { expect } from 'chai'; -import { SiteInfo } from '../site'; -import { PosResolver } from './pos-resolver'; - -describe('resolvePointOfSale', () => { - it('should return empty when no point of sale present', () => { - const site: SiteInfo = { - name: 'no-pos', - hostName: 'www.nopos.com', - language: 'en', - }; - const language = 'en'; - const result = PosResolver.resolve(site, language); - expect(result).to.equal(''); - }); - - it('should return pos for provided language', () => { - const myPoint = 'apos.com'; - const site: SiteInfo = { - name: 'apos', - hostName: 'www.apos.com', - pointOfSale: { - en: myPoint, - }, - language: 'de-DE', - }; - - const result = PosResolver.resolve(site, 'en'); - expect(result).to.equal(myPoint); - }); - - it('should use fallback wildcard value as first backup', () => { - const site: SiteInfo = { - name: 'apos', - hostName: 'www.apos.com', - pointOfSale: { - 'de-DE': 'depos.com', - 'es-ES': 'espos.com', - '*': 'fallpos.com', - }, - language: 'de-DE', - }; - - const result = PosResolver.resolve(site, 'en'); - expect(result).to.equal('fallpos.com'); - }); - - it('should return pos for site language as second backup', () => { - const site: SiteInfo = { - name: 'apos', - hostName: 'www.apos.com', - pointOfSale: { - 'de-DE': 'depos.com', - 'es-ES': 'espos.com', - }, - language: 'de-DE', - }; - - const result = PosResolver.resolve(site, 'en'); - expect(result).to.equal('depos.com'); - }); - - it('should return pos for site language when provided language is empty', () => { - const site: SiteInfo = { - name: 'apos', - hostName: 'www.apos.com', - pointOfSale: { - 'de-DE': 'depos.com', - 'es-ES': 'espos.com', - }, - language: 'de-DE', - }; - - const result = PosResolver.resolve(site, ''); - expect(result).to.equal('depos.com'); - }); -}); diff --git a/packages/sitecore-jss/src/personalize/pos-resolver.ts b/packages/sitecore-jss/src/personalize/pos-resolver.ts deleted file mode 100644 index 4c0e2e8510..0000000000 --- a/packages/sitecore-jss/src/personalize/pos-resolver.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { SiteInfo } from '../site'; - -export class PosResolver { - static resolve = (site: SiteInfo, language: string) => { - return site.pointOfSale - ? site.pointOfSale[language] || site.pointOfSale['*'] || site.pointOfSale[site.language] - : ''; - }; -} 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 d9c95db7d4..3423bafe71 100644 --- a/packages/sitecore-jss/src/site/graphql-siteinfo-service.test.ts +++ b/packages/sitecore-jss/src/site/graphql-siteinfo-service.test.ts @@ -12,17 +12,14 @@ describe('GraphQLSiteInfoService', () => { name, hostName, language, - pointOfSale = '', }: { name: string; hostName: string; language: string; - pointOfSale: string; }): GraphQLSiteInfoResult => ({ name: { value: name }, hostName: { value: hostName }, language: { value: language }, - pointOfSale: { value: pointOfSale }, }); const nonEmptyResponse = ({ @@ -45,7 +42,6 @@ describe('GraphQLSiteInfoService', () => { name: `site ${start + n}`, hostName: 'restricted.gov', language: 'en', - pointOfSale: 'en=en-pos', }) ), ...sites, @@ -81,7 +77,6 @@ describe('GraphQLSiteInfoService', () => { name: 'public 0', hostName: 'pr.showercurtains.org', language: '', - pointOfSale: '', }), ], }) @@ -93,15 +88,11 @@ describe('GraphQLSiteInfoService', () => { name: 'site 0', hostName: 'restricted.gov', language: 'en', - pointOfSale: { - en: 'en-pos', - }, }, { name: 'public 0', hostName: 'pr.showercurtains.org', language: '', - pointOfSale: undefined, }, ]); }); @@ -114,7 +105,6 @@ describe('GraphQLSiteInfoService', () => { name: 'public 0', hostName: 'pr.showercurtains.org', language: '', - pointOfSale: '', }), ], }) @@ -130,15 +120,11 @@ describe('GraphQLSiteInfoService', () => { name: 'site 0', hostName: 'restricted.gov', language: 'en', - pointOfSale: { - en: 'en-pos', - }, }, { name: 'public 0', hostName: 'pr.showercurtains.org', language: '', - pointOfSale: undefined, }, ]); }); @@ -159,49 +145,31 @@ describe('GraphQLSiteInfoService', () => { name: 'site 0', hostName: 'restricted.gov', language: 'en', - pointOfSale: { - en: 'en-pos', - }, }, { name: 'site 1', hostName: 'restricted.gov', language: 'en', - pointOfSale: { - en: 'en-pos', - }, }, { name: 'site 2', hostName: 'restricted.gov', language: 'en', - pointOfSale: { - en: 'en-pos', - }, }, { name: 'site 3', hostName: 'restricted.gov', language: 'en', - pointOfSale: { - en: 'en-pos', - }, }, { name: 'site 4', hostName: 'restricted.gov', language: 'en', - pointOfSale: { - en: 'en-pos', - }, }, { name: 'site 5', hostName: 'restricted.gov', language: 'en', - pointOfSale: { - en: 'en-pos', - }, }, ]); }); @@ -235,7 +203,6 @@ describe('GraphQLSiteInfoService', () => { name: 'public 0', hostName: 'pr.showercurtains.org', language: '', - pointOfSale: '', }), ], }) @@ -251,15 +218,11 @@ describe('GraphQLSiteInfoService', () => { name: 'site 0', hostName: 'restricted.gov', language: 'en', - pointOfSale: { - en: 'en-pos', - }, }, { name: 'public 0', hostName: 'pr.showercurtains.org', language: '', - pointOfSale: undefined, }, ]); nock.cleanAll(); diff --git a/packages/sitecore-jss/src/site/graphql-siteinfo-service.ts b/packages/sitecore-jss/src/site/graphql-siteinfo-service.ts index 8c57ca256c..87d14bd244 100644 --- a/packages/sitecore-jss/src/site/graphql-siteinfo-service.ts +++ b/packages/sitecore-jss/src/site/graphql-siteinfo-service.ts @@ -1,4 +1,3 @@ -import { URLSearchParams } from 'url'; import { GraphQLClient, GraphQLRequestClient, PageInfo } from '../graphql'; import debug from '../debug'; import { CacheClient, CacheOptions, MemoryCacheClient } from '../cache-client'; @@ -34,9 +33,6 @@ const defaultQuery = /* GraphQL */ ` language: field(name: "Language") { value } - pointOfSale: field(name: "POS") { - value - } } } } @@ -60,10 +56,6 @@ export type SiteInfo = { * Site default language */ language: string; - /** - * Site point of sale - */ - pointOfSale?: Record; }; export type GraphQLSiteInfoServiceConfig = CacheOptions & { @@ -107,9 +99,6 @@ export type GraphQLSiteInfoResult = { language: { value: string; }; - pointOfSale?: { - value: string; - }; }; export class GraphQLSiteInfoService { @@ -146,9 +135,6 @@ export class GraphQLSiteInfoService { }); const result = response?.search?.results?.reduce((result, current) => { result.push({ - pointOfSale: current.pointOfSale?.value - ? Object.fromEntries(new URLSearchParams(current.pointOfSale.value)) - : undefined, name: current.name.value, hostName: current.hostName.value, language: current.language.value, From 6fe1dcfd18fcb4b06452c26b6829e5a191e680b4 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Thu, 2 Nov 2023 20:52:12 +0200 Subject: [PATCH 02/37] Handle async Events init call --- .../templates/nextjs-personalize/package.json | 2 +- .../src/components/CdpPageView.tsx | 41 ++------------ .../src/lib/context/events.ts | 17 ++++++ .../src/lib/context/plugins/events.ts | 23 ++++++++ .../src/lib/middleware/plugins/personalize.ts | 3 +- .../src/lib/site-resolver/plugins/default.ts | 26 --------- .../nextjs/scripts/generate-plugins.ts | 5 ++ .../nextjs/src/byoc/context/index.ts | 41 ++++++++++++++ .../src/templates/nextjs/src/byoc/index.ts | 7 +-- .../templates/nextjs/src/lib/context/index.ts | 56 +++++++++++++++++++ .../nextjs/src/pages/[[...path]].tsx | 8 +++ .../src/middleware/personalize-middleware.ts | 1 - 12 files changed, 160 insertions(+), 70 deletions(-) create mode 100644 packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/events.ts create mode 100644 packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts delete mode 100644 packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/site-resolver/plugins/default.ts create mode 100644 packages/create-sitecore-jss/src/templates/nextjs/src/byoc/context/index.ts create mode 100644 packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/package.json b/packages/create-sitecore-jss/src/templates/nextjs-personalize/package.json index 6653194865..f6cd2d66aa 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/package.json +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "@sitecore/engage": "^1.4.1" + "@sitecore-cloudsdk/events": "^0.1.0-rc.0" } } diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/components/CdpPageView.tsx b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/components/CdpPageView.tsx index 190c0af1d7..73a905d8d7 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/components/CdpPageView.tsx +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/components/CdpPageView.tsx @@ -1,54 +1,23 @@ import { CdpHelper, LayoutServicePageState, - SiteInfo, useSitecoreContext, - PosResolver, } from '@sitecore-jss/sitecore-jss-nextjs'; import { useEffect } from 'react'; import config from 'temp/config'; -import { init } from '@sitecore/engage'; -import { siteResolver } from 'lib/site-resolver'; +import { createPageView } from 'lib/context/events'; /** * This is the CDP page view component. * It uses the Sitecore Engage SDK to enable page view events on the client-side. * See Sitecore Engage SDK documentation for details. - * https://www.npmjs.com/package/@sitecore/engage + * https://www.npmjs.com/package/@sitecore-cloudsdk/events */ const CdpPageView = (): JSX.Element => { const { sitecoreContext: { pageState, route, variantId, site }, } = useSitecoreContext(); - /** - * Creates a page view event using the Sitecore Engage SDK. - */ - const createPageView = async ( - page: string, - language: string, - site: SiteInfo, - pageVariantId: string - ) => { - const pointOfSale = PosResolver.resolve(site, language); - const engage = await init({ - clientKey: process.env.NEXT_PUBLIC_CDP_CLIENT_KEY || '', - targetURL: process.env.NEXT_PUBLIC_CDP_TARGET_URL || '', - // Replace with the top level cookie domain of the website that is being integrated e.g ".example.com" and not "www.example.com" - cookieDomain: window.location.hostname.replace(/^www\./, ''), - // Cookie may be created in personalize middleware (server), but if not we should create it here - forceServerCookieMode: false, - }); - engage.pageView({ - channel: 'WEB', - currency: 'USD', - pointOfSale, - page, - pageVariantId, - language, - }); - }; - /** * Determines if the page view events should be turned off. * IMPORTANT: You should implement based on your cookie consent management solution of choice. @@ -63,12 +32,11 @@ const CdpPageView = (): JSX.Element => { if (pageState !== LayoutServicePageState.Normal || !route?.itemId) { return; } - // Do not create events if disabled (e.g. we don't have consent) + // // Do not create events if disabled (e.g. we don't have consent) if (disabled()) { return; } - const siteInfo = siteResolver.getByName(site?.name || config.siteName); const language = route.itemLanguage || config.defaultLanguage; const scope = process.env.NEXT_PUBLIC_PERSONALIZE_SCOPE; @@ -78,7 +46,8 @@ const CdpPageView = (): JSX.Element => { variantId as string, scope ); - createPageView(route.name, language, siteInfo, pageVariantId); + + createPageView(route.name, language, pageVariantId); }, [pageState, route, variantId, site]); return <>; diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/events.ts b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/events.ts new file mode 100644 index 0000000000..e07b182e66 --- /dev/null +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/events.ts @@ -0,0 +1,17 @@ +import { pageView } from '@sitecore-cloudsdk/events'; +import { contextReady } from '.'; + +/** + * Creates a page view event using the Sitecore Engage SDK. + */ +export const createPageView = async (page: string, language: string, pageVariantId: string) => { + contextReady?.then(() => { + pageView({ + channel: 'WEB', + currency: 'USD', + page, + pageVariantId, + language, + }); + }); +}; diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts new file mode 100644 index 0000000000..100a7e9bd0 --- /dev/null +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts @@ -0,0 +1,23 @@ +import * as Events from '@sitecore-cloudsdk/events'; +import { Plugin, Props, Context } from '..'; + +class EventsPlugin implements Plugin { + order = 1; + + async exec(props: Props, context: Context) { + if (typeof window === 'undefined') return; + + await Events.init({ + siteName: props.site, + sitecoreEdgeContextId: context.sitecoreEdgeContextId || '5555', + // Replace with the top level cookie domain of the website that is being integrated e.g ".example.com" and not "www.example.com" + cookieDomain: window.location.hostname.replace(/^www\./, ''), + // Cookie may be created in personalize middleware (server), but if not we should create it here + enableBrowserCookie: true, + }); + + context.SDK.Events = Events; + } +} + +export const eventsPlugin = new EventsPlugin(); diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/middleware/plugins/personalize.ts b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/middleware/plugins/personalize.ts index 6e48968b43..9f969bd9e0 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/middleware/plugins/personalize.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/middleware/plugins/personalize.ts @@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { PersonalizeMiddleware } from '@sitecore-jss/sitecore-jss-nextjs/middleware'; import { MiddlewarePlugin } from '..'; import clientFactory from 'lib/graphql-client-factory'; +import config from 'temp/config'; import { siteResolver } from 'lib/site-resolver'; /** @@ -33,7 +34,7 @@ class PersonalizePlugin implements MiddlewarePlugin { }, // Configuration for your Sitecore CDP endpoint cdpConfig: { - sitecoreContextId: config.sitecoreEdgeContextId, + sitecoreEdgeContextId: config.sitecoreEdgeContextId, timeout: (process.env.PERSONALIZE_MIDDLEWARE_CDP_TIMEOUT && parseInt(process.env.PERSONALIZE_MIDDLEWARE_CDP_TIMEOUT)) || diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/site-resolver/plugins/default.ts b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/site-resolver/plugins/default.ts deleted file mode 100644 index 5244f8e268..0000000000 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/site-resolver/plugins/default.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { SiteInfo } from '@sitecore-jss/sitecore-jss-nextjs/site'; -import { tryParseEnvValue } from '@sitecore-jss/sitecore-jss-nextjs/utils'; -import config from 'temp/config'; -import { SiteResolverPlugin } from '..'; - -// Resolving from env variable, but it can be expanded or change in future if needed. -const pointOfSale = tryParseEnvValue>( - process.env.NEXT_PUBLIC_CDP_POINTOFSALE, - { [config.defaultLanguage]: process.env.NEXT_PUBLIC_CDP_POINTOFSALE || '' } -); - -class DefaultPlugin implements SiteResolverPlugin { - exec(sites: SiteInfo[]): SiteInfo[] { - // Add default/configured site - sites.unshift({ - name: config.siteName, - language: config.defaultLanguage, - hostName: '*', - pointOfSale, - }); - - return sites; - } -} - -export const defaultPlugin = new DefaultPlugin(); diff --git a/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-plugins.ts b/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-plugins.ts index 46e59e7a8a..ec470c98ba 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-plugins.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-plugins.ts @@ -57,6 +57,11 @@ const pluginDefinitions: PluginDefinition[] = [ rootPath: 'src/lib/site-resolver/plugins', moduleType: ModuleType.ESM, }, + { + distPath: 'src/temp/context-plugins.ts', + rootPath: 'src/lib/context/plugins', + moduleType: ModuleType.ESM, + }, ]; pluginDefinitions.forEach(definition => { diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/byoc/context/index.ts b/packages/create-sitecore-jss/src/templates/nextjs/src/byoc/context/index.ts new file mode 100644 index 0000000000..153cd2f126 --- /dev/null +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/byoc/context/index.ts @@ -0,0 +1,41 @@ +import { SiteInfo } from '@sitecore-jss/sitecore-jss-nextjs'; +import * as FEAAS from '@sitecore-feaas/clientside/react'; +import * as plugins from 'temp/byoc/context-plugins'; +import config from 'temp/config'; + +export interface Props { + site: SiteInfo; +} + +export interface Context { + sitecoreEdgeUrl: string; + sitecoreEdgeContextId: string; + SDK: { [key: string]: unknown }; +} + +export interface Plugin { + /** + * Detect order when the plugin should be called, e.g. 0 - will be called first (can be a plugin which has to be initialized before other plugins) + */ + order: number; + /** + * A function which will be called during the SDK initialization + */ + exec(props: Props, context: Context): Promise; +} + +export const context: Context = { + sitecoreEdgeUrl: config.sitecoreEdgeUrl, + sitecoreEdgeContextId: config.sitecoreEdgeContextId, + SDK: {}, +}; + +export const setBYOCContext = async (props: Props) => { + await (Object.values(plugins) as Plugin[]) + .sort((p1, p2) => p1.order - p2.order) + .reduce(async (_, plugin) => { + await plugin.exec(props, context); + }, Promise.resolve()); + + FEAAS.setContextProperties(context); +}; diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/byoc/index.ts b/packages/create-sitecore-jss/src/templates/nextjs/src/byoc/index.ts index aeb744d961..3e9486da3f 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/byoc/index.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/byoc/index.ts @@ -1,6 +1,6 @@ import * as FEAAS from '@sitecore-feaas/clientside/react'; import dynamic from 'next/dynamic'; -import config from 'temp/config'; +import { context } from 'lib/context'; /** * This is an out-of-box bundler for External components (BYOC) (see Sitecore documentation for more details) * It enables registering components in client-only or SSR/hybrid contexts @@ -8,10 +8,7 @@ import config from 'temp/config'; */ // Set context properties to be available within BYOC components -FEAAS.setContextProperties({ - sitecoreEdgeUrl: config.sitecoreEdgeUrl, - sitecoreEdgeContextId: config.sitecoreEdgeContextId, -}); +FEAAS.setContextProperties(context); // Import your client-only components via client-bundle. Nextjs's dynamic() call will ensure they are only rendered client-side const ClientBundle = dynamic(() => import('./index.client'), { diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts new file mode 100644 index 0000000000..98874208f6 --- /dev/null +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts @@ -0,0 +1,56 @@ +import * as FEAAS from '@sitecore-feaas/clientside/react'; +import * as plugins from 'temp/context-plugins'; +import config from 'temp/config'; + +export interface Props { + site: string; +} + +export interface Context { + sitecoreEdgeUrl: string; + sitecoreEdgeContextId: string; + SDK: { [key: string]: unknown }; +} + +export interface Plugin { + /** + * Detect order when the plugin should be called, e.g. 0 - will be called first (can be a plugin which has to be initialized before other plugins) + */ + order: number; + /** + * A function which will be called during the SDK initialization + */ + exec(props: Props, context: Context): Promise; +} + +export const context: Context = { + sitecoreEdgeUrl: config.sitecoreEdgeUrl, + sitecoreEdgeContextId: config.sitecoreEdgeContextId, + SDK: {}, +}; + +let initialized = false; + +export let contextReady: Promise | undefined; + +export const initContext = async (props: Props) => { + if (initialized) return; + + console.log('CONTEXT INIT IS CALLED'); + + initialized = true; + + contextReady = new Promise(async (resolve) => { + await (Object.values(plugins) as Plugin[]) + .sort((p1, p2) => p1.order - p2.order) + .reduce(async (_, plugin) => { + await plugin.exec(props, context); + }, Promise.resolve()); + + FEAAS.setContextProperties(context); + + console.log('CONTEXT IS INITIALIZED', context); + + resolve(); + }); +}; diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/pages/[[...path]].tsx b/packages/create-sitecore-jss/src/templates/nextjs/src/pages/[[...path]].tsx index 97e6a91de3..da1c1fb203 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/pages/[[...path]].tsx +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/pages/[[...path]].tsx @@ -19,6 +19,9 @@ import { handleEditorFastRefresh } from '@sitecore-jss/sitecore-jss-nextjs/utils import { SitecorePageProps } from 'lib/page-props'; import { sitecorePagePropsFactory } from 'lib/page-props-factory'; import { componentBuilder } from 'temp/componentBuilder'; +import { initContext } from 'src/lib/context'; +import { siteResolver } from 'lib/site-resolver'; +import config from 'temp/config'; <% if (prerender === 'SSG') { -%> import { sitemapFetcher } from 'lib/sitemap-fetcher'; @@ -35,6 +38,11 @@ const SitecorePage = ({ notFound, componentProps, layoutData, headLinks }: Sitec return ; } + const site = layoutData.sitecore.context.site; + const siteInfo = siteResolver.getByName(site?.name || config.siteName); + + initContext({ site: siteInfo.name }); + const isEditing = layoutData.sitecore.context.pageEditing; const isComponentRendering = layoutData.sitecore.context.renderingType === RenderingType.Component; diff --git a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts index 0ff1e588e0..1dd2d000b0 100644 --- a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts +++ b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts @@ -8,7 +8,6 @@ import { import { debug } from '@sitecore-jss/sitecore-jss'; import { MiddlewareBase, MiddlewareBaseConfig } from './middleware'; import { initServer, personalizeServer } from '@sitecore-cloudsdk/personalize'; -import { init, initServer } from '@sitecore-cloudsdk/events'; export type CdpServiceConfig = { From 8672755a6530bc8ab49bc4725925a2458bb8321a Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Fri, 3 Nov 2023 14:04:30 +0200 Subject: [PATCH 03/37] Update --- .../src/lib/context/plugins/events.ts | 2 +- .../nextjs/src/byoc/context/index.ts | 41 ------------------- .../templates/nextjs/src/lib/context/index.ts | 4 -- 3 files changed, 1 insertion(+), 46 deletions(-) delete mode 100644 packages/create-sitecore-jss/src/templates/nextjs/src/byoc/context/index.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts index 100a7e9bd0..3fbf89f1d7 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts @@ -9,7 +9,7 @@ class EventsPlugin implements Plugin { await Events.init({ siteName: props.site, - sitecoreEdgeContextId: context.sitecoreEdgeContextId || '5555', + sitecoreEdgeContextId: context.sitecoreEdgeContextId, // Replace with the top level cookie domain of the website that is being integrated e.g ".example.com" and not "www.example.com" cookieDomain: window.location.hostname.replace(/^www\./, ''), // Cookie may be created in personalize middleware (server), but if not we should create it here diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/byoc/context/index.ts b/packages/create-sitecore-jss/src/templates/nextjs/src/byoc/context/index.ts deleted file mode 100644 index 153cd2f126..0000000000 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/byoc/context/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { SiteInfo } from '@sitecore-jss/sitecore-jss-nextjs'; -import * as FEAAS from '@sitecore-feaas/clientside/react'; -import * as plugins from 'temp/byoc/context-plugins'; -import config from 'temp/config'; - -export interface Props { - site: SiteInfo; -} - -export interface Context { - sitecoreEdgeUrl: string; - sitecoreEdgeContextId: string; - SDK: { [key: string]: unknown }; -} - -export interface Plugin { - /** - * Detect order when the plugin should be called, e.g. 0 - will be called first (can be a plugin which has to be initialized before other plugins) - */ - order: number; - /** - * A function which will be called during the SDK initialization - */ - exec(props: Props, context: Context): Promise; -} - -export const context: Context = { - sitecoreEdgeUrl: config.sitecoreEdgeUrl, - sitecoreEdgeContextId: config.sitecoreEdgeContextId, - SDK: {}, -}; - -export const setBYOCContext = async (props: Props) => { - await (Object.values(plugins) as Plugin[]) - .sort((p1, p2) => p1.order - p2.order) - .reduce(async (_, plugin) => { - await plugin.exec(props, context); - }, Promise.resolve()); - - FEAAS.setContextProperties(context); -}; diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts index 98874208f6..55d205a40c 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts @@ -36,8 +36,6 @@ export let contextReady: Promise | undefined; export const initContext = async (props: Props) => { if (initialized) return; - console.log('CONTEXT INIT IS CALLED'); - initialized = true; contextReady = new Promise(async (resolve) => { @@ -49,8 +47,6 @@ export const initContext = async (props: Props) => { FEAAS.setContextProperties(context); - console.log('CONTEXT IS INITIALIZED', context); - resolve(); }); }; From 890922a74aa80d74ba079f15be13696d5040117a Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Fri, 3 Nov 2023 14:20:07 +0200 Subject: [PATCH 04/37] Update --- .../src/components/CdpPageView.tsx | 4 +-- .../src/lib/context/events.ts | 3 ++- .../src/lib/context/plugins/events.ts | 7 ++++- .../templates/nextjs/src/lib/context/index.ts | 26 +++++++++++++++++++ 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/components/CdpPageView.tsx b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/components/CdpPageView.tsx index 73a905d8d7..2fe3c0e19c 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/components/CdpPageView.tsx +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/components/CdpPageView.tsx @@ -9,7 +9,7 @@ import { createPageView } from 'lib/context/events'; /** * This is the CDP page view component. - * It uses the Sitecore Engage SDK to enable page view events on the client-side. + * It uses the Sitecore Cloud SDK to enable page view events on the client-side. * See Sitecore Engage SDK documentation for details. * https://www.npmjs.com/package/@sitecore-cloudsdk/events */ @@ -32,7 +32,7 @@ const CdpPageView = (): JSX.Element => { if (pageState !== LayoutServicePageState.Normal || !route?.itemId) { return; } - // // Do not create events if disabled (e.g. we don't have consent) + // Do not create events if disabled (e.g. we don't have consent) if (disabled()) { return; } diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/events.ts b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/events.ts index e07b182e66..db757bca9b 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/events.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/events.ts @@ -2,7 +2,8 @@ import { pageView } from '@sitecore-cloudsdk/events'; import { contextReady } from '.'; /** - * Creates a page view event using the Sitecore Engage SDK. + * Creates a page view event using the Sitecore Cloud SDK Events. + * This function is used to trigger a pageView event. The event will be sent once the Sitecore Context is initialized. */ export const createPageView = async (page: string, language: string, pageVariantId: string) => { contextReady?.then(() => { diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts index 3fbf89f1d7..50dfee06cf 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts @@ -1,10 +1,15 @@ import * as Events from '@sitecore-cloudsdk/events'; import { Plugin, Props, Context } from '..'; +/** + * This is Events plugin for Context initialization. + * It is used to enable Cloud SDK Events in Next.js. + */ class EventsPlugin implements Plugin { - order = 1; + order = 0; async exec(props: Props, context: Context) { + // Events module can't be initialized on the server side if (typeof window === 'undefined') return; await Events.init({ diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts index 55d205a40c..b06fb99a25 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts @@ -2,10 +2,17 @@ import * as FEAAS from '@sitecore-feaas/clientside/react'; import * as plugins from 'temp/context-plugins'; import config from 'temp/config'; +/** + * Plugin's incoming properties + */ export interface Props { site: string; } + +/** + * Context definition + */ export interface Context { sitecoreEdgeUrl: string; sitecoreEdgeContextId: string; @@ -23,6 +30,13 @@ export interface Plugin { exec(props: Props, context: Context): Promise; } + +/** + * Context object to be utilized during the initialization of BYOC components. + * This context is an essential part of BYOC component initialization and provides necessary configuration and information + * to set up and customize BYOC components. It serves as the foundation for integrating and configuring BYOC features + * within your application. + */ export const context: Context = { sitecoreEdgeUrl: config.sitecoreEdgeUrl, sitecoreEdgeContextId: config.sitecoreEdgeContextId, @@ -31,8 +45,20 @@ export const context: Context = { let initialized = false; +/** + * An identifier used by other SDKs to ensure that a function is executed only after the Context is initialized. + * This identifier serves as a synchronization mechanism to coordinate the initialization and execution of functions in different SDKs. + * It helps ensure that a specific action or operation is performed only when the application's context has been fully initialized + * and is ready to support the required functionality. + * Defined once @function initContext is called + */ export let contextReady: Promise | undefined; +/** + * Initializes the application Context and associated Software Development Kits (SDKs). + * This function is the entry point for setting up the application's context and any SDKs that are required for its proper functioning. + * It prepares the resources needed to interact with various services and features within the application. + */ export const initContext = async (props: Props) => { if (initialized) return; From f6bc981f5e5bb6f179d9ed14a85424823785f31d Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Fri, 3 Nov 2023 14:24:26 +0200 Subject: [PATCH 05/37] Updated test --- .../middleware/personalize-middleware.test.ts | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.test.ts b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.test.ts index 54a8ab35a6..88afe54fe7 100644 --- a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.test.ts +++ b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.test.ts @@ -210,25 +210,6 @@ describe('PersonalizeMiddleware', () => { }) )); - // { - // handleCookie: async () => { - // return props.handleCookieStub || Promise.resolve(); - // }, - // event: async () => { - // return Promise.resolve(null); - // }, - // personalize: async () => { - // return Promise.resolve({ variantId: props.variantId }); - // }, - // identity: async () => { - // return Promise.resolve(null); - // }, - // pageView: async () => { - // return Promise.resolve(null); - // }, - // version: '1.0', - // } - const getPersonalizeInfo = (middleware['personalizeService']['getPersonalizeInfo'] = props.getPersonalizeInfoStub || sinon.stub().returns( From 0fe146c4d080839526b69f95494c1c424d5cb055 Mon Sep 17 00:00:00 2001 From: Adam Brauer <400763+ambrauer@users.noreply.github.com> Date: Fri, 3 Nov 2023 15:05:58 -0500 Subject: [PATCH 06/37] rename nextjs-personalize -> nextjs-xmcloud. move feaas and BYOC here. --- .../templates/nextjs-personalize/package.json | 5 --- .../.env | 40 +++++++++---------- .../src/templates/nextjs-xmcloud/package.json | 7 ++++ .../plugins/feaas.ts | 0 .../scaffold-component/plugins/byoc.ts | 0 .../plugins/next-steps-byoc.ts | 0 .../scripts/templates/byoc-component-src.ts | 0 .../src/Scripts.tsx | 3 ++ .../src/byoc/index.client.tsx | 0 .../src/byoc/index.hybrid.ts | 0 .../src/byoc/index.ts | 0 .../src/components/CdpPageView.tsx | 0 .../lib/extract-path/plugins/personalize.ts | 0 .../src/lib/middleware/plugins/personalize.ts | 0 .../src/lib/next-config/plugins/feaas.js | 0 .../src/lib/next-config/plugins/monorepo.js | 30 ++++++++++++++ .../plugins/feaas-themes.ts | 0 .../page-props-factory/plugins/personalize.ts | 0 .../src/lib/site-resolver/plugins/default.ts | 0 .../src/templates/nextjs/package.json | 2 - .../src/templates/nextjs/src/Layout.tsx | 3 -- .../src/lib/next-config/plugins/monorepo.js | 9 ----- 22 files changed, 60 insertions(+), 39 deletions(-) delete mode 100644 packages/create-sitecore-jss/src/templates/nextjs-personalize/package.json rename packages/create-sitecore-jss/src/templates/{nextjs-personalize => nextjs-xmcloud}/.env (97%) create mode 100644 packages/create-sitecore-jss/src/templates/nextjs-xmcloud/package.json rename packages/create-sitecore-jss/src/templates/{nextjs => nextjs-xmcloud}/scripts/generate-component-builder/plugins/feaas.ts (100%) rename packages/create-sitecore-jss/src/templates/{nextjs => nextjs-xmcloud}/scripts/scaffold-component/plugins/byoc.ts (100%) rename packages/create-sitecore-jss/src/templates/{nextjs => nextjs-xmcloud}/scripts/scaffold-component/plugins/next-steps-byoc.ts (100%) rename packages/create-sitecore-jss/src/templates/{nextjs => nextjs-xmcloud}/scripts/templates/byoc-component-src.ts (100%) rename packages/create-sitecore-jss/src/templates/{nextjs-personalize => nextjs-xmcloud}/src/Scripts.tsx (52%) rename packages/create-sitecore-jss/src/templates/{nextjs => nextjs-xmcloud}/src/byoc/index.client.tsx (100%) rename packages/create-sitecore-jss/src/templates/{nextjs => nextjs-xmcloud}/src/byoc/index.hybrid.ts (100%) rename packages/create-sitecore-jss/src/templates/{nextjs => nextjs-xmcloud}/src/byoc/index.ts (100%) rename packages/create-sitecore-jss/src/templates/{nextjs-personalize => nextjs-xmcloud}/src/components/CdpPageView.tsx (100%) rename packages/create-sitecore-jss/src/templates/{nextjs-personalize => nextjs-xmcloud}/src/lib/extract-path/plugins/personalize.ts (100%) rename packages/create-sitecore-jss/src/templates/{nextjs-personalize => nextjs-xmcloud}/src/lib/middleware/plugins/personalize.ts (100%) rename packages/create-sitecore-jss/src/templates/{nextjs => nextjs-xmcloud}/src/lib/next-config/plugins/feaas.js (100%) create mode 100644 packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/next-config/plugins/monorepo.js rename packages/create-sitecore-jss/src/templates/{nextjs => nextjs-xmcloud}/src/lib/page-props-factory/plugins/feaas-themes.ts (100%) rename packages/create-sitecore-jss/src/templates/{nextjs-personalize => nextjs-xmcloud}/src/lib/page-props-factory/plugins/personalize.ts (100%) rename packages/create-sitecore-jss/src/templates/{nextjs-personalize => nextjs-xmcloud}/src/lib/site-resolver/plugins/default.ts (100%) diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/package.json b/packages/create-sitecore-jss/src/templates/nextjs-personalize/package.json deleted file mode 100644 index 6653194865..0000000000 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "@sitecore/engage": "^1.4.1" - } -} diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/.env b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/.env similarity index 97% rename from packages/create-sitecore-jss/src/templates/nextjs-personalize/.env rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/.env index a508d9edfb..ff31a3ebdd 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/.env +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/.env @@ -1,20 +1,20 @@ -# Your Sitecore CDP API target (specific to your data center region) -NEXT_PUBLIC_CDP_TARGET_URL= - -# Your Sitecore CDP client key -NEXT_PUBLIC_CDP_CLIENT_KEY= - -# An optional Sitecore Personalize scope identifier. -# This can be used to isolate personalization data when multiple XM Cloud Environments share a Personalize tenant. -# This should match the PAGES_PERSONALIZE_SCOPE environment variable for your connected XM Cloud Environment. -NEXT_PUBLIC_PERSONALIZE_SCOPE= - -# Your Sitecore CDP point(s) of sale -# Can be provided as a single value (mypoint.com) or a multi-value JSON with locales ({"en":"en.mypoint.com","fr":"fr.mypoint.com"} etc) -NEXT_PUBLIC_CDP_POINTOFSALE= - -# Timeout (ms) for Sitecore CDP requests to respond within. Default is 400. -PERSONALIZE_MIDDLEWARE_CDP_TIMEOUT= - -# Timeout (ms) for Sitecore Experience Edge requests to respond within. Default is 400. -PERSONALIZE_MIDDLEWARE_EDGE_TIMEOUT= +# Your Sitecore CDP API target (specific to your data center region) +NEXT_PUBLIC_CDP_TARGET_URL= + +# Your Sitecore CDP client key +NEXT_PUBLIC_CDP_CLIENT_KEY= + +# An optional Sitecore Personalize scope identifier. +# This can be used to isolate personalization data when multiple XM Cloud Environments share a Personalize tenant. +# This should match the PAGES_PERSONALIZE_SCOPE environment variable for your connected XM Cloud Environment. +NEXT_PUBLIC_PERSONALIZE_SCOPE= + +# Your Sitecore CDP point(s) of sale +# Can be provided as a single value (mypoint.com) or a multi-value JSON with locales ({"en":"en.mypoint.com","fr":"fr.mypoint.com"} etc) +NEXT_PUBLIC_CDP_POINTOFSALE= + +# Timeout (ms) for Sitecore CDP requests to respond within. Default is 400. +PERSONALIZE_MIDDLEWARE_CDP_TIMEOUT= + +# Timeout (ms) for Sitecore Experience Edge requests to respond within. Default is 400. +PERSONALIZE_MIDDLEWARE_EDGE_TIMEOUT= diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/package.json b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/package.json new file mode 100644 index 0000000000..33d06644d2 --- /dev/null +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "@sitecore/components": "~1.0.19", + "@sitecore/engage": "^1.4.1", + "@sitecore-feaas/clientside": "^0.4.12" + } +} diff --git a/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-component-builder/plugins/feaas.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/scripts/generate-component-builder/plugins/feaas.ts similarity index 100% rename from packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-component-builder/plugins/feaas.ts rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/scripts/generate-component-builder/plugins/feaas.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs/scripts/scaffold-component/plugins/byoc.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/scripts/scaffold-component/plugins/byoc.ts similarity index 100% rename from packages/create-sitecore-jss/src/templates/nextjs/scripts/scaffold-component/plugins/byoc.ts rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/scripts/scaffold-component/plugins/byoc.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs/scripts/scaffold-component/plugins/next-steps-byoc.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/scripts/scaffold-component/plugins/next-steps-byoc.ts similarity index 100% rename from packages/create-sitecore-jss/src/templates/nextjs/scripts/scaffold-component/plugins/next-steps-byoc.ts rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/scripts/scaffold-component/plugins/next-steps-byoc.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs/scripts/templates/byoc-component-src.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/scripts/templates/byoc-component-src.ts similarity index 100% rename from packages/create-sitecore-jss/src/templates/nextjs/scripts/templates/byoc-component-src.ts rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/scripts/templates/byoc-component-src.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/Scripts.tsx b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/Scripts.tsx similarity index 52% rename from packages/create-sitecore-jss/src/templates/nextjs-personalize/src/Scripts.tsx rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/Scripts.tsx index 52ecf0b89a..8f7e0602a0 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/Scripts.tsx +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/Scripts.tsx @@ -1,8 +1,11 @@ +// The BYOC bundle imports external (BYOC) components into the app and makes sure they are ready to be used +import BYOC from 'src/byoc'; import CdpPageView from 'components/CdpPageView'; const Scripts = (): JSX.Element => { return ( <> + ); diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/byoc/index.client.tsx b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/index.client.tsx similarity index 100% rename from packages/create-sitecore-jss/src/templates/nextjs/src/byoc/index.client.tsx rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/index.client.tsx diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/byoc/index.hybrid.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/index.hybrid.ts similarity index 100% rename from packages/create-sitecore-jss/src/templates/nextjs/src/byoc/index.hybrid.ts rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/index.hybrid.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/byoc/index.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/index.ts similarity index 100% rename from packages/create-sitecore-jss/src/templates/nextjs/src/byoc/index.ts rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/index.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/components/CdpPageView.tsx b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/components/CdpPageView.tsx similarity index 100% rename from packages/create-sitecore-jss/src/templates/nextjs-personalize/src/components/CdpPageView.tsx rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/components/CdpPageView.tsx diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/extract-path/plugins/personalize.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/extract-path/plugins/personalize.ts similarity index 100% rename from packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/extract-path/plugins/personalize.ts rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/extract-path/plugins/personalize.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/middleware/plugins/personalize.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/middleware/plugins/personalize.ts similarity index 100% rename from packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/middleware/plugins/personalize.ts rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/middleware/plugins/personalize.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/next-config/plugins/feaas.js b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/next-config/plugins/feaas.js similarity index 100% rename from packages/create-sitecore-jss/src/templates/nextjs/src/lib/next-config/plugins/feaas.js rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/next-config/plugins/feaas.js diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/next-config/plugins/monorepo.js b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/next-config/plugins/monorepo.js new file mode 100644 index 0000000000..f1ce7c9a26 --- /dev/null +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/next-config/plugins/monorepo.js @@ -0,0 +1,30 @@ +const path = require('path'); +const CWD = process.cwd(); + +/** + * @param {import('next').NextConfig} nextConfig + */ +const monorepoPlugin = (nextConfig = {}) => { + return Object.assign({}, nextConfig, { + webpack: (config, options) => { + if (options.isServer) { + config.externals = ['react', 'vertx', ...config.externals]; + } + // Monorepo support for @sitecore-feaas/clientside/react + config.resolve.alias['@sitecore-feaas/clientside/react'] = path.resolve( + CWD, options.isServer ? + './node_modules/@sitecore-feaas/clientside/dist/node/react.cjs' : + './node_modules/@sitecore-feaas/clientside/dist/browser/react.esm.js' + ); + + // Overload the Webpack config if it was already overloaded + if (typeof nextConfig.webpack === 'function') { + return nextConfig.webpack(config, options); + } + + return config; + } + }); +}; + +module.exports = monorepoPlugin; diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/page-props-factory/plugins/feaas-themes.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/page-props-factory/plugins/feaas-themes.ts similarity index 100% rename from packages/create-sitecore-jss/src/templates/nextjs/src/lib/page-props-factory/plugins/feaas-themes.ts rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/page-props-factory/plugins/feaas-themes.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/page-props-factory/plugins/personalize.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/page-props-factory/plugins/personalize.ts similarity index 100% rename from packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/page-props-factory/plugins/personalize.ts rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/page-props-factory/plugins/personalize.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/site-resolver/plugins/default.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/site-resolver/plugins/default.ts similarity index 100% rename from packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/site-resolver/plugins/default.ts rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/site-resolver/plugins/default.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs/package.json b/packages/create-sitecore-jss/src/templates/nextjs/package.json index 1b70571a3e..ae59f8e832 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/package.json +++ b/packages/create-sitecore-jss/src/templates/nextjs/package.json @@ -29,8 +29,6 @@ }, "license": "Apache-2.0", "dependencies": { - "@sitecore-feaas/clientside": "^0.4.12", - "@sitecore/components": "~1.0.19", "@sitecore-jss/sitecore-jss-nextjs": "~21.7.0-canary", "graphql": "~15.8.0", "graphql-tag": "^2.12.6", diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/Layout.tsx b/packages/create-sitecore-jss/src/templates/nextjs/src/Layout.tsx index 80f6c90924..c3679bd1b0 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/Layout.tsx +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/Layout.tsx @@ -9,8 +9,6 @@ import { import { getPublicUrl } from '@sitecore-jss/sitecore-jss-nextjs/utils'; import Navigation from 'src/Navigation'; import Scripts from 'src/Scripts'; -// The bundle imports external (BYOC) components into the app and makes sure they are ready to be used. -import BYOC from 'src/byoc'; // Prefix public assets with a public URL to enable compatibility with Sitecore editors. // If you're not supporting Sitecore editors, you can remove this. @@ -34,7 +32,6 @@ const Layout = ({ layoutData, headLinks }: LayoutProps): JSX.Element => { return ( <> - {fields.pageTitle.value.toString() || 'Page'} diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/next-config/plugins/monorepo.js b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/next-config/plugins/monorepo.js index f1ce7c9a26..f6f54147ea 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/next-config/plugins/monorepo.js +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/next-config/plugins/monorepo.js @@ -1,6 +1,3 @@ -const path = require('path'); -const CWD = process.cwd(); - /** * @param {import('next').NextConfig} nextConfig */ @@ -10,12 +7,6 @@ const monorepoPlugin = (nextConfig = {}) => { if (options.isServer) { config.externals = ['react', 'vertx', ...config.externals]; } - // Monorepo support for @sitecore-feaas/clientside/react - config.resolve.alias['@sitecore-feaas/clientside/react'] = path.resolve( - CWD, options.isServer ? - './node_modules/@sitecore-feaas/clientside/dist/node/react.cjs' : - './node_modules/@sitecore-feaas/clientside/dist/browser/react.esm.js' - ); // Overload the Webpack config if it was already overloaded if (typeof nextConfig.webpack === 'function') { From a2dfa991de689e69f026a943131c4d03f6bf89c6 Mon Sep 17 00:00:00 2001 From: Adam Brauer <400763+ambrauer@users.noreply.github.com> Date: Fri, 3 Nov 2023 16:02:11 -0500 Subject: [PATCH 07/37] Move Sitecore Edge Platform / conext related items to nextjs-xmcloud --- .../scripts/config/plugins/multisite.ts | 15 ++------- .../src/templates/nextjs-xmcloud/.env | 9 ++++++ .../scripts/config/plugins/edge-platform.ts | 29 +++++++++++++++++ .../src/lib/graphql-client-factory/create.ts | 32 +++++++++++++++++++ .../src/templates/nextjs/.env | 10 +----- .../nextjs/scripts/config/plugins/fallback.ts | 10 ------ .../nextjs/scripts/generate-config.ts | 2 -- .../src/templates/nextjs/src/lib/config.ts | 2 -- .../src/lib/graphql-client-factory/create.ts | 11 ++----- 9 files changed, 77 insertions(+), 43 deletions(-) create mode 100644 packages/create-sitecore-jss/src/templates/nextjs-xmcloud/scripts/config/plugins/edge-platform.ts create mode 100644 packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/graphql-client-factory/create.ts 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 754717a41f..dce37363b5 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 @@ -15,19 +15,10 @@ class MultisitePlugin implements ConfigPlugin { async exec(config: JssConfig) { let sites: SiteInfo[] = []; - const endpoint = config.sitecoreEdgeContextId ? config.sitecoreEdgeUrl : config.graphQLEndpoint; - - if (!endpoint || process.env.SITECORE) { - console.warn( - chalk.yellow( - `Skipping site information fetch (${ - !endpoint ? 'missing GraphQL endpoint' : 'building on XM Cloud' - })` - ) - ); + if (process.env.SITECORE) { + console.warn(chalk.yellow('Skipping site information fetch (building on XM Cloud)')); } else { - console.log(`Fetching site information from ${endpoint}`); - + console.log('Fetching site information'); try { const siteInfoService = new GraphQLSiteInfoService({ clientFactory: createGraphQLClientFactory(config), diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/.env b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/.env index ff31a3ebdd..45ccca7ccc 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/.env +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/.env @@ -1,3 +1,12 @@ + +# ========== Sitecore Edge Platform =========== + +# Your unified Sitecore Edge Context Id. +# This will be used over any Sitecore Preview / Delivery Edge variables (above). +SITECORE_EDGE_CONTEXT_ID= + +# ============================================== + # Your Sitecore CDP API target (specific to your data center region) NEXT_PUBLIC_CDP_TARGET_URL= diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/scripts/config/plugins/edge-platform.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/scripts/config/plugins/edge-platform.ts new file mode 100644 index 0000000000..fb190f516b --- /dev/null +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/scripts/config/plugins/edge-platform.ts @@ -0,0 +1,29 @@ +import 'dotenv/config'; +import chalk from 'chalk'; +import { constantCase } from 'constant-case'; +import { JssConfig } from 'lib/config'; +import { ConfigPlugin } from '..'; + +/** + * This plugin will set config props used by the Sitecore Edge Platform. + */ +class EdgePlatformPlugin implements ConfigPlugin { + order = 2; + + async exec(config: JssConfig) { + if (config.sitecoreApiKey && config.sitecoreEdgeContextId) { + console.log( + chalk.yellow( + "You have configured both 'sitecoreApiKey' and 'sitecoreEdgeContextId' values. The 'sitecoreEdgeContextId' is used instead." + ) + ); + } + + return Object.assign({}, config, { + sitecoreEdgeUrl: process.env[`${constantCase('sitecoreEdgeUrl')}`] || 'https://edge-platform.sitecorecloud.io', + sitecoreEdgeContextId: process.env[`${constantCase('sitecoreEdgeContextId')}`], + }); + } +} + +export const edgePlatformPlugin = new EdgePlatformPlugin(); diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/graphql-client-factory/create.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/graphql-client-factory/create.ts new file mode 100644 index 0000000000..bc76efc74f --- /dev/null +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/graphql-client-factory/create.ts @@ -0,0 +1,32 @@ +import { + GraphQLRequestClientFactoryConfig, + GraphQLRequestClient, + getEdgeProxyContentUrl +} from '@sitecore-jss/sitecore-jss-nextjs/graphql'; +import { JssConfig } from 'lib/config'; + +/** + * Creates a new GraphQLRequestClientFactory instance + * @param config jss config + * @returns GraphQLRequestClientFactory instance + */ +export const createGraphQLClientFactory = (config: JssConfig) => { + let clientConfig: GraphQLRequestClientFactoryConfig; + + if (config.sitecoreEdgeContextId) { + clientConfig = { + endpoint: getEdgeProxyContentUrl(config.sitecoreEdgeContextId, config.sitecoreEdgeUrl), + }; + } else if (config.graphQLEndpoint && config.sitecoreApiKey) { + clientConfig = { + endpoint: config.graphQLEndpoint, + apiKey: config.sitecoreApiKey, + }; + } else { + throw new Error( + 'Please configure either your sitecoreEdgeContextId, or your graphQLEndpoint and sitecoreApiKey.' + ); + } + + return GraphQLRequestClient.createClientFactory(clientConfig); +}; diff --git a/packages/create-sitecore-jss/src/templates/nextjs/.env b/packages/create-sitecore-jss/src/templates/nextjs/.env index 17c41a657f..102e902528 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/.env +++ b/packages/create-sitecore-jss/src/templates/nextjs/.env @@ -17,15 +17,7 @@ PUBLIC_URL=http://localhost:3000 # We recommend an alphanumeric value of at least 16 characters. JSS_EDITING_SECRET= -# ===== Sitecore Edge Platform ====== - -# Your unified Sitecore Edge Context Id. -SITECORE_EDGE_CONTEXT_ID= - -# ================================ - # ====== Sitecore Preview / Delivery Edge ====== -# (Sitecore Edge Proxy environment variables should be set empty, otherwise they will be prioritized and applied) # Your Sitecore API key is needed to build the app. Typically, the API key is # defined in `scjssconfig.json` (as `sitecore.apiKey`). This file may not exist @@ -46,7 +38,7 @@ SITECORE_API_HOST= # the resolved Sitecore API hostname + the `graphQLEndpointPath` defined in your `package.json`. GRAPH_QL_ENDPOINT= -# ================================ +# ============================================== # Your Sitecore site name. # Uses your `package.json` config `appName` if empty. diff --git a/packages/create-sitecore-jss/src/templates/nextjs/scripts/config/plugins/fallback.ts b/packages/create-sitecore-jss/src/templates/nextjs/scripts/config/plugins/fallback.ts index ba3b08c00a..535847cf59 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/scripts/config/plugins/fallback.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs/scripts/config/plugins/fallback.ts @@ -1,4 +1,3 @@ -import chalk from 'chalk'; import { JssConfig } from 'lib/config'; import { ConfigPlugin } from '..'; @@ -11,19 +10,10 @@ class FallbackPlugin implements ConfigPlugin { order = 100; async exec(config: JssConfig) { - if (config.sitecoreApiKey && config.sitecoreEdgeContextId) { - console.log( - chalk.yellow( - "You have configured both 'sitecoreApiKey' and 'sitecoreEdgeContextId' values. The 'sitecoreEdgeContextId' is used instead." - ) - ); - } - return Object.assign({}, config, { defaultLanguage: config.defaultLanguage || 'en', sitecoreApiKey: config.sitecoreApiKey || 'no-api-key-set', layoutServiceConfigurationName: config.layoutServiceConfigurationName || 'default', - sitecoreEdgeUrl: config.sitecoreEdgeUrl || 'https://edge-platform.sitecorecloud.io', }); } } diff --git a/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-config.ts b/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-config.ts index 67c7c5b0f8..d8f3c4de10 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-config.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-config.ts @@ -14,8 +14,6 @@ import { jssConfigFactory } from './config'; const defaultConfig: JssConfig = { sitecoreApiKey: process.env[`${constantCase('sitecoreApiKey')}`], sitecoreApiHost: process.env[`${constantCase('sitecoreApiHost')}`], - sitecoreEdgeUrl: process.env[`${constantCase('sitecoreEdgeUrl')}`], - sitecoreEdgeContextId: process.env[`${constantCase('sitecoreEdgeContextId')}`], siteName: process.env[`${constantCase('siteName')}`] || process.env[`${constantCase('jssAppName')}`], graphQLEndpointPath: process.env[`${constantCase('graphQLEndpointPath')}`], diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/config.ts b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/config.ts index 88ec0aaa07..83a2596423 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/config.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/config.ts @@ -4,8 +4,6 @@ export interface JssConfig extends Record { sitecoreApiKey?: string; sitecoreApiHost?: string; - sitecoreEdgeUrl?: string; - sitecoreEdgeContextId?: string; siteName?: string; graphQLEndpointPath?: string; defaultLanguage?: string; diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/graphql-client-factory/create.ts b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/graphql-client-factory/create.ts index bc76efc74f..b3bf7017bb 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/graphql-client-factory/create.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/graphql-client-factory/create.ts @@ -1,7 +1,6 @@ import { GraphQLRequestClientFactoryConfig, - GraphQLRequestClient, - getEdgeProxyContentUrl + GraphQLRequestClient } from '@sitecore-jss/sitecore-jss-nextjs/graphql'; import { JssConfig } from 'lib/config'; @@ -13,18 +12,14 @@ import { JssConfig } from 'lib/config'; export const createGraphQLClientFactory = (config: JssConfig) => { let clientConfig: GraphQLRequestClientFactoryConfig; - if (config.sitecoreEdgeContextId) { - clientConfig = { - endpoint: getEdgeProxyContentUrl(config.sitecoreEdgeContextId, config.sitecoreEdgeUrl), - }; - } else if (config.graphQLEndpoint && config.sitecoreApiKey) { + if (config.graphQLEndpoint && config.sitecoreApiKey) { clientConfig = { endpoint: config.graphQLEndpoint, apiKey: config.sitecoreApiKey, }; } else { throw new Error( - 'Please configure either your sitecoreEdgeContextId, or your graphQLEndpoint and sitecoreApiKey.' + 'Please configure your graphQLEndpoint and sitecoreApiKey.' ); } From 4cbe74011004846460e00550e4b66b0a8a0119bd Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Mon, 6 Nov 2023 13:56:52 +0200 Subject: [PATCH 08/37] Updated context initialization --- .../src/lib/context/events.ts | 14 ++--- .../src/lib/context/plugins/events.ts | 9 ++- .../templates/nextjs/src/lib/context/index.ts | 57 ++++++++++--------- packages/sitecore-jss-nextjs/package.json | 6 +- .../src/middleware/personalize-middleware.ts | 6 +- .../src/graphql/graphql-edge-proxy.test.ts | 4 +- .../src/graphql/graphql-edge-proxy.ts | 2 +- 7 files changed, 52 insertions(+), 46 deletions(-) diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/events.ts b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/events.ts index db757bca9b..9ad5f5c279 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/events.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/events.ts @@ -1,18 +1,18 @@ -import { pageView } from '@sitecore-cloudsdk/events'; -import { contextReady } from '.'; +import * as EventsSDK from '@sitecore-cloudsdk/events/browser'; +import { whenSDKReady } from './'; /** * Creates a page view event using the Sitecore Cloud SDK Events. - * This function is used to trigger a pageView event. The event will be sent once the Sitecore Context is initialized. + * This function is used to trigger a pageView event. The event will be sent once the SDK is initialized. */ export const createPageView = async (page: string, language: string, pageVariantId: string) => { - contextReady?.then(() => { - pageView({ + return whenSDKReady('Events').then((Events) => + Events.pageView({ channel: 'WEB', currency: 'USD', page, pageVariantId, language, - }); - }); + }) + ); }; diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts index 50dfee06cf..c9075abb48 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts @@ -1,4 +1,4 @@ -import * as Events from '@sitecore-cloudsdk/events'; +import * as Events from '@sitecore-cloudsdk/events/browser'; import { Plugin, Props, Context } from '..'; /** @@ -8,6 +8,11 @@ import { Plugin, Props, Context } from '..'; class EventsPlugin implements Plugin { order = 0; + SDK = { + name: 'Events', + lib: Events, + }; + async exec(props: Props, context: Context) { // Events module can't be initialized on the server side if (typeof window === 'undefined') return; @@ -20,8 +25,6 @@ class EventsPlugin implements Plugin { // Cookie may be created in personalize middleware (server), but if not we should create it here enableBrowserCookie: true, }); - - context.SDK.Events = Events; } } diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts index b06fb99a25..322e3eefc7 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts @@ -9,7 +9,6 @@ export interface Props { site: string; } - /** * Context definition */ @@ -25,34 +24,34 @@ export interface Plugin { */ order: number; /** - * A function which will be called during the SDK initialization + * A function to be called during SDK initialization. */ exec(props: Props, context: Context): Promise; + /** + * The SDK associated with the plugin. + */ + SDK: { + name: string; + lib: unknown; + }; } - -/** - * Context object to be utilized during the initialization of BYOC components. - * This context is an essential part of BYOC component initialization and provides necessary configuration and information - * to set up and customize BYOC components. It serves as the foundation for integrating and configuring BYOC features - * within your application. - */ export const context: Context = { sitecoreEdgeUrl: config.sitecoreEdgeUrl, sitecoreEdgeContextId: config.sitecoreEdgeContextId, SDK: {}, }; -let initialized = false; +// Initialize promise list +export const promises: { [key: string]: Promise } = {}; -/** - * An identifier used by other SDKs to ensure that a function is executed only after the Context is initialized. - * This identifier serves as a synchronization mechanism to coordinate the initialization and execution of functions in different SDKs. - * It helps ensure that a specific action or operation is performed only when the application's context has been fully initialized - * and is ready to support the required functionality. - * Defined once @function initContext is called - */ -export let contextReady: Promise | undefined; +// Helper to wait for asynchronosuly initialized SDK +export function whenSDKReady(name: string): Promise { + return promises[name] as Promise; +} + +// Indicates whether the Context and SDK(s) have been initialized. +let initialized = false; /** * Initializes the application Context and associated Software Development Kits (SDKs). @@ -64,15 +63,19 @@ export const initContext = async (props: Props) => { initialized = true; - contextReady = new Promise(async (resolve) => { - await (Object.values(plugins) as Plugin[]) - .sort((p1, p2) => p1.order - p2.order) - .reduce(async (_, plugin) => { - await plugin.exec(props, context); - }, Promise.resolve()); + const pluginList = Object.values(plugins).sort((p1, p2) => p1.order - p2.order) as Plugin[]; + + for (const plugin of pluginList) { + promises[plugin.SDK.name] = new Promise(async (resolve) => { + await plugin.exec(props, context); + + context.SDK[plugin.SDK.name] = plugin.SDK.lib; + + resolve(plugin.SDK.lib); + }); - FEAAS.setContextProperties(context); + await promises[plugin.SDK.name]; + } - resolve(); - }); + FEAAS.setContextProperties(context); }; diff --git a/packages/sitecore-jss-nextjs/package.json b/packages/sitecore-jss-nextjs/package.json index 78a9e52185..91f509429b 100644 --- a/packages/sitecore-jss-nextjs/package.json +++ b/packages/sitecore-jss-nextjs/package.json @@ -30,7 +30,7 @@ "url": "https://github.com/sitecore/jss/issues" }, "devDependencies": { - "@sitecore-cloudsdk/personalize": "^0.1.0-rc.0", + "@sitecore-cloudsdk/personalize": "^0.1.0-rc.4", "@types/chai": "^4.3.4", "@types/chai-as-promised": "^7.1.5", "@types/chai-string": "^1.4.2", @@ -66,8 +66,8 @@ "typescript": "~4.9.4" }, "peerDependencies": { - "@sitecore-cloudsdk/events": "^0.1.0-rc.0", - "@sitecore-cloudsdk/personalize": "^0.1.0-rc.0", + "@sitecore-cloudsdk/events": "^0.1.0-rc.4", + "@sitecore-cloudsdk/personalize": "^0.1.0-rc.4", "next": "^13.4.16", "react": "^18.2.0", "react-dom": "^18.2.0" diff --git a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts index 1dd2d000b0..09139b3c6c 100644 --- a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts +++ b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts @@ -7,7 +7,7 @@ import { } from '@sitecore-jss/sitecore-jss/personalize'; import { debug } from '@sitecore-jss/sitecore-jss'; import { MiddlewareBase, MiddlewareBaseConfig } from './middleware'; -import { initServer, personalizeServer } from '@sitecore-cloudsdk/personalize'; +import { init, personalize } from '@sitecore-cloudsdk/personalize/server'; export type CdpServiceConfig = { @@ -103,7 +103,7 @@ export class PersonalizeMiddleware extends MiddlewareBase { request: NextRequest; response: NextResponse; }): Promise { - await initServer( + await init( { sitecoreEdgeContextId, siteName, @@ -134,7 +134,7 @@ export class PersonalizeMiddleware extends MiddlewareBase { language, }; - return (await personalizeServer(personalizationData, request, timeout)) as { + return (await personalize(personalizationData, request, timeout)) as { variantId: string; }; } diff --git a/packages/sitecore-jss/src/graphql/graphql-edge-proxy.test.ts b/packages/sitecore-jss/src/graphql/graphql-edge-proxy.test.ts index c847d26d6d..b2a53976b4 100644 --- a/packages/sitecore-jss/src/graphql/graphql-edge-proxy.test.ts +++ b/packages/sitecore-jss/src/graphql/graphql-edge-proxy.test.ts @@ -11,7 +11,7 @@ describe('graphql-edge-proxy', () => { const url = getEdgeProxyContentUrl(sitecoreEdgeContextId); expect(url).to.equal( - `${SITECORE_EDGE_URL_DEFAULT}/content/api/graphql/v1?sitecoreContextId=0730fc5a-3333-5555-5555-08db6d7ddb49` + `${SITECORE_EDGE_URL_DEFAULT}/v1/content/api/graphql/v1?sitecoreContextId=0730fc5a-3333-5555-5555-08db6d7ddb49` ); }); @@ -22,7 +22,7 @@ describe('graphql-edge-proxy', () => { const url = getEdgeProxyContentUrl(sitecoreEdgeContextId, sitecoreEdgeUrl); expect(url).to.equal( - 'https://test.com/content/api/graphql/v1?sitecoreContextId=0730fc5a-3333-5555-5555-08db6d7ddb49' + 'https://test.com/v1/content/api/graphql/v1?sitecoreContextId=0730fc5a-3333-5555-5555-08db6d7ddb49' ); }); }); diff --git a/packages/sitecore-jss/src/graphql/graphql-edge-proxy.ts b/packages/sitecore-jss/src/graphql/graphql-edge-proxy.ts index b869b71944..abb0ce734b 100644 --- a/packages/sitecore-jss/src/graphql/graphql-edge-proxy.ts +++ b/packages/sitecore-jss/src/graphql/graphql-edge-proxy.ts @@ -9,4 +9,4 @@ import { SITECORE_EDGE_URL_DEFAULT } from '../constants'; export const getEdgeProxyContentUrl = ( sitecoreEdgeContextId: string, sitecoreEdgeUrl = SITECORE_EDGE_URL_DEFAULT -) => `${sitecoreEdgeUrl}/content/api/graphql/v1?sitecoreContextId=${sitecoreEdgeContextId}`; +) => `${sitecoreEdgeUrl}/v1/content/api/graphql/v1?sitecoreContextId=${sitecoreEdgeContextId}`; From 3d64d0a2526c40ead270f137167098d870159d07 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Mon, 6 Nov 2023 14:17:26 +0200 Subject: [PATCH 09/37] Updated dependency --- .../src/templates/nextjs-personalize/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/package.json b/packages/create-sitecore-jss/src/templates/nextjs-personalize/package.json index f6cd2d66aa..3d38508632 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/package.json +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "@sitecore-cloudsdk/events": "^0.1.0-rc.0" + "@sitecore-cloudsdk/events": "^0.1.0-rc.4" } } From 657b08ef49ce3cfcd126950bb420dc8c92cb0072 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Mon, 6 Nov 2023 14:28:40 +0200 Subject: [PATCH 10/37] Updated Yarn.lock --- yarn.lock | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index ede60e9b5f..3eb431985c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6433,6 +6433,32 @@ __metadata: languageName: node linkType: hard +"@sitecore-cloudsdk/core@npm:^0.1.0-rc.2": + version: 0.1.0-rc.4 + resolution: "@sitecore-cloudsdk/core@npm:0.1.0-rc.4" + dependencies: + "@sitecore-cloudsdk/utils": ^0.1.0-rc.2 + checksum: 596ebb923e8a7598b15c7762c6a8c3f7e6bc1fb9746e8ae51c656a7c3dd91a39e75b089abb8f4a1784f6dba2cf4115d9947f9fada26d6022191985acb4ee28a5 + languageName: node + linkType: hard + +"@sitecore-cloudsdk/personalize@npm:^0.1.0-rc.4": + version: 0.1.0-rc.4 + resolution: "@sitecore-cloudsdk/personalize@npm:0.1.0-rc.4" + dependencies: + "@sitecore-cloudsdk/core": ^0.1.0-rc.2 + "@sitecore-cloudsdk/utils": ^0.1.0-rc.2 + checksum: 87623064f786234dfc03072d695d988059f65327d3371d9280e03d904228a0f75733c0bf109dce6de0d9a51a49ce0ee9feee21c22b757e0ae04747267897f4aa + languageName: node + linkType: hard + +"@sitecore-cloudsdk/utils@npm:^0.1.0-rc.2": + version: 0.1.0-rc.4 + resolution: "@sitecore-cloudsdk/utils@npm:0.1.0-rc.4" + checksum: 28bce2648560a6b9e0621228642cc590baddc3ad1c335acec9ba0c45934472c1a3455048f9d9e4a566f933044fb1cc39c8b59f0ec1e60e878e9337f4227b72e5 + languageName: node + linkType: hard + "@sitecore-feaas/clientside@npm:^0.4.12": version: 0.4.12 resolution: "@sitecore-feaas/clientside@npm:0.4.12" @@ -6620,10 +6646,10 @@ __metadata: version: 0.0.0-use.local resolution: "@sitecore-jss/sitecore-jss-nextjs@workspace:packages/sitecore-jss-nextjs" dependencies: + "@sitecore-cloudsdk/personalize": ^0.1.0-rc.4 "@sitecore-jss/sitecore-jss": 21.6.0-canary.18 "@sitecore-jss/sitecore-jss-dev-tools": 21.6.0-canary.18 "@sitecore-jss/sitecore-jss-react": 21.6.0-canary.18 - "@sitecore/engage": ^1.4.1 "@types/chai": ^4.3.4 "@types/chai-as-promised": ^7.1.5 "@types/chai-string": ^1.4.2 @@ -6663,7 +6689,8 @@ __metadata: ts-node: ^10.9.1 typescript: ~4.9.4 peerDependencies: - "@sitecore/engage": ^1.4.1 + "@sitecore-cloudsdk/events": ^0.1.0-rc.4 + "@sitecore-cloudsdk/personalize": ^0.1.0-rc.4 next: ^13.4.16 react: ^18.2.0 react-dom: ^18.2.0 @@ -6914,13 +6941,6 @@ __metadata: languageName: node linkType: hard -"@sitecore/engage@npm:^1.4.1": - version: 1.4.1 - resolution: "@sitecore/engage@npm:1.4.1" - checksum: 582b7a55ba407765def12518114790fb1735c359e60fe4594102a5e643539b148a494b02bf71970bcafc807b92fe19a1ab8ff7c2af3db2c080ab36ec2eb16d57 - languageName: node - linkType: hard - "@socket.io/component-emitter@npm:~3.1.0": version: 3.1.0 resolution: "@socket.io/component-emitter@npm:3.1.0" From 244fa5bbcc00854a12dcf760d407c42b540453b3 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Mon, 6 Nov 2023 15:20:39 +0200 Subject: [PATCH 11/37] Fix lint errors --- .../middleware/personalize-middleware.test.ts | 149 ++++++++++++------ .../src/middleware/personalize-middleware.ts | 31 ++-- 2 files changed, 119 insertions(+), 61 deletions(-) diff --git a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.test.ts b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.test.ts index 88afe54fe7..7618545b66 100644 --- a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.test.ts +++ b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.test.ts @@ -429,7 +429,12 @@ describe('PersonalizeMiddleware', () => { it('no variant identified', async () => { const req = createRequest(); const res = createResponse(); - const { middleware, getPersonalizeInfo, initPersonalizeServer, personalize } = createMiddleware({ + const { + middleware, + getPersonalizeInfo, + initPersonalizeServer, + personalize, + } = createMiddleware({ variantId: undefined, }); const headers = {}; @@ -451,11 +456,15 @@ describe('PersonalizeMiddleware', () => { const req = createRequest(); const res = createResponse(); const handleCookieStub = sinon.stub().resolves(); - const { middleware, getPersonalizeInfo, initPersonalizeServer, personalize } = - createMiddleware({ - variantId: 'invalid-variant', - handleCookieStub, - }); + const { + middleware, + getPersonalizeInfo, + initPersonalizeServer, + personalize, + } = createMiddleware({ + variantId: 'invalid-variant', + handleCookieStub, + }); const finalRes = await middleware.getHandler()(req, res); const headers = {}; req.headers.forEach((value, key) => (headers[key] = value)); @@ -485,15 +494,20 @@ describe('PersonalizeMiddleware', () => { }); const res = createResponse(); const nextRewriteStub = sinon.stub(nextjs.NextResponse, 'rewrite').returns(res); - const { middleware, getPersonalizeInfo, siteResolver, initPersonalizeServer, personalize } = - createMiddleware({ - language, - variantId: 'variant-2', - personalizeInfo: { - variantIds, - contentId, - }, - }); + const { + middleware, + getPersonalizeInfo, + siteResolver, + initPersonalizeServer, + personalize, + } = createMiddleware({ + language, + variantId: 'variant-2', + personalizeInfo: { + variantIds, + contentId, + }, + }); const finalRes = await middleware.getHandler()(req, res); validateDebugLog('personalize middleware start: %o', { @@ -529,10 +543,15 @@ describe('PersonalizeMiddleware', () => { }); const res = createResponse(); const nextRewriteStub = sinon.stub(nextjs.NextResponse, 'rewrite').returns(res); - const { middleware, getPersonalizeInfo, siteResolver, initPersonalizeServer, personalize } = - createMiddleware({ - variantId: 'variant-2', - }); + const { + middleware, + getPersonalizeInfo, + siteResolver, + initPersonalizeServer, + personalize, + } = createMiddleware({ + variantId: 'variant-2', + }); const finalRes = await middleware.getHandler()(req, res); validateDebugLog('personalize middleware start: %o', { @@ -563,10 +582,15 @@ describe('PersonalizeMiddleware', () => { const req = createRequest(); const res = createResponse(); const nextRewriteStub = sinon.stub(nextjs.NextResponse, 'rewrite').returns(res); - const { middleware, getPersonalizeInfo, siteResolver, initPersonalizeServer, personalize } = - createMiddleware({ - variantId: 'variant-2', - }); + const { + middleware, + getPersonalizeInfo, + siteResolver, + initPersonalizeServer, + personalize, + } = createMiddleware({ + variantId: 'variant-2', + }); const finalRes = await middleware.getHandler()(req); expect(getPersonalizeInfo.calledWith('/styleguide', 'en')).to.be.true; @@ -598,10 +622,15 @@ describe('PersonalizeMiddleware', () => { const req = createRequest({ headerValues: { referer: null } }); const res = createResponse(); const nextRewriteStub = sinon.stub(nextjs.NextResponse, 'rewrite').returns(res); - const { middleware, getPersonalizeInfo, siteResolver, initPersonalizeServer, personalize } = - createMiddleware({ - variantId: 'variant-2', - }); + const { + middleware, + getPersonalizeInfo, + siteResolver, + initPersonalizeServer, + personalize, + } = createMiddleware({ + variantId: 'variant-2', + }); const finalRes = await middleware.getHandler()(req, res); validateDebugLog('personalize middleware start: %o', { @@ -636,10 +665,15 @@ describe('PersonalizeMiddleware', () => { }, }); const nextRewriteStub = sinon.stub(nextjs.NextResponse, 'rewrite').returns(res); - const { middleware, getPersonalizeInfo, initPersonalizeServer, personalize, siteResolver } = - createMiddleware({ - variantId: 'variant-2', - }); + const { + middleware, + getPersonalizeInfo, + initPersonalizeServer, + personalize, + siteResolver, + } = createMiddleware({ + variantId: 'variant-2', + }); const finalRes = await middleware.getHandler()(req, res); validateDebugLog('personalize middleware start: %o', { @@ -675,10 +709,15 @@ describe('PersonalizeMiddleware', () => { }, }); const nextRewriteStub = sinon.stub(nextjs.NextResponse, 'rewrite').returns(res); - const { middleware, getPersonalizeInfo, initPersonalizeServer, personalize, siteResolver } = - createMiddleware({ - variantId: 'variant-2', - }); + const { + middleware, + getPersonalizeInfo, + initPersonalizeServer, + personalize, + siteResolver, + } = createMiddleware({ + variantId: 'variant-2', + }); const finalRes = await middleware.getHandler()(req, res); validateDebugLog('personalize middleware start: %o', { @@ -713,10 +752,15 @@ describe('PersonalizeMiddleware', () => { }); const res = createResponse(); const nextRewriteStub = sinon.stub(nextjs.NextResponse, 'rewrite').returns(res); - const { middleware, getPersonalizeInfo, initPersonalizeServer, personalize, siteResolver } = - createMiddleware({ - variantId: 'variant-2', - }); + const { + middleware, + getPersonalizeInfo, + initPersonalizeServer, + personalize, + siteResolver, + } = createMiddleware({ + variantId: 'variant-2', + }); const finalRes = await middleware.getHandler()(req, res); validateDebugLog('personalize middleware start: %o', { @@ -751,11 +795,16 @@ describe('PersonalizeMiddleware', () => { }); const res = createResponse(); const nextRewriteStub = sinon.stub(nextjs.NextResponse, 'rewrite').returns(res); - const { middleware, getPersonalizeInfo, initPersonalizeServer, personalize, siteResolver } = - createMiddleware({ - variantId: 'variant-2', - defaultHostname: 'foobar', - }); + const { + middleware, + getPersonalizeInfo, + initPersonalizeServer, + personalize, + siteResolver, + } = createMiddleware({ + variantId: 'variant-2', + defaultHostname: 'foobar', + }); const finalRes = await middleware.getHandler()(req, res); expect(initPersonalizeServer.calledOnce).to.be.true; expect(personalize.calledOnce).to.be.true; @@ -805,10 +854,14 @@ describe('PersonalizeMiddleware', () => { const getPersonalizeInfoWithError = sinon.stub().throws(error); - const { middleware, getPersonalizeInfo, initPersonalizeServer, personalize } = - createMiddleware({ - getPersonalizeInfoStub: getPersonalizeInfoWithError, - }); + const { + middleware, + getPersonalizeInfo, + initPersonalizeServer, + personalize, + } = createMiddleware({ + getPersonalizeInfoStub: getPersonalizeInfoWithError, + }); const finalRes = await middleware.getHandler()(req, res); diff --git a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts index 09139b3c6c..c115b74b19 100644 --- a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts +++ b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts @@ -9,7 +9,6 @@ import { debug } from '@sitecore-jss/sitecore-jss'; import { MiddlewareBase, MiddlewareBaseConfig } from './middleware'; import { init, personalize } from '@sitecore-cloudsdk/personalize/server'; - export type CdpServiceConfig = { /** * Your unified Sitecore Edge Context Id @@ -115,17 +114,20 @@ export class PersonalizeMiddleware extends MiddlewareBase { ); } - protected async personalize({ - params, - personalizeInfo, - language, - timeout, - }: { - personalizeInfo: PersonalizeInfo; - params: ExperienceParams, - language: string; - timeout?: number; - }, request: NextRequest) { + protected async personalize( + { + params, + personalizeInfo, + language, + timeout, + }: { + personalizeInfo: PersonalizeInfo; + params: ExperienceParams; + language: string; + timeout?: number; + }, + request: NextRequest + ) { const personalizationData = { channel: this.config.cdpConfig.channel || 'WEB', currency: this.config.cdpConfig.currency ?? 'USA', @@ -231,7 +233,10 @@ export class PersonalizeMiddleware extends MiddlewareBase { // Execute targeted experience in Personalize SDK // eslint-disable-next-line no-useless-catch try { - const personalization = await this.personalize({ personalizeInfo, params, language, timeout }, req); + const personalization = await this.personalize( + { personalizeInfo, params, language, timeout }, + req + ); variantId = personalization.variantId; } catch (error) { throw error; From 2c95fd0e2d954f7a50f0be96feaca6e74ef4cf56 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Mon, 6 Nov 2023 15:28:13 +0200 Subject: [PATCH 12/37] Updated CHANGELOG --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20382448f7..4e830b5e85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,12 @@ Our versioning strategy is as follows: * `[sitecore-jss-react]` Fix PlaceholderCommon with using two and more dynamic placeholders. ([#1641](https://github.com/Sitecore/jss/pull/1641)) * `[templates/nextjs-multisite]` Fix site info fetch errors (now skipped) on XM Cloud rendering/editing host builds. ([#1649](https://github.com/Sitecore/jss/pull/1649)) +### 🛠 Breaking Changes + +* `[templates/nextjs]` `[sitecore-jss-nextjs]` CloudSDK Integration ([#1652](https://github.com/Sitecore/jss/pull/1652)): + * Removed the following properties from _PersonalizeMiddleware_: _getPointOfSale_, _clientKey_, _endpoint_. You now need to provide _sitecoreEdgeContextId_ as a replacement. + * _PersonalizeMiddleware_ has transitioned to utilizing the _CloudSDK_ package, replacing the previous dependency on _Engage_. + ## 21.5.0 ### 🐛 Bug Fixes From 2df0a004f453ac3f0a11be36eecd64cdf73fa817 Mon Sep 17 00:00:00 2001 From: Adam Brauer <400763+ambrauer@users.noreply.github.com> Date: Mon, 6 Nov 2023 15:21:26 -0600 Subject: [PATCH 13/37] Repurpose nextjs-personalize -> nextjs-xmcloud initializer "system" template (driven by prompt / --xmcloud CLI option) --- .../nextjs-styleguide-tracking/index.ts | 6 +- .../index.ts | 9 +-- .../src/initializers/nextjs/index.ts | 71 ++++++++++++------- .../src/initializers/nextjs/prompts.ts | 19 ++--- 4 files changed, 60 insertions(+), 45 deletions(-) rename packages/create-sitecore-jss/src/initializers/{nextjs-personalize => nextjs-xmcloud}/index.ts (73%) diff --git a/packages/create-sitecore-jss/src/initializers/nextjs-styleguide-tracking/index.ts b/packages/create-sitecore-jss/src/initializers/nextjs-styleguide-tracking/index.ts index 05a4163a18..81fe0dd663 100644 --- a/packages/create-sitecore-jss/src/initializers/nextjs-styleguide-tracking/index.ts +++ b/packages/create-sitecore-jss/src/initializers/nextjs-styleguide-tracking/index.ts @@ -35,10 +35,10 @@ export default class NextjsStyleguideInitializer implements Initializer { } if ( - args.templates.includes('nextjs-personalize') || - pkg.config?.templates?.includes('nextjs-personalize') + args.templates.includes('nextjs-xmcloud') || + pkg.config?.templates?.includes('nextjs-xmcloud') ) { - console.log(incompatibleAddonsMsg('nextjs-styleguide-tracking', 'nextjs-personalize')); + console.log(incompatibleAddonsMsg('nextjs-styleguide-tracking', 'nextjs-xmcloud')); } const response = { diff --git a/packages/create-sitecore-jss/src/initializers/nextjs-personalize/index.ts b/packages/create-sitecore-jss/src/initializers/nextjs-xmcloud/index.ts similarity index 73% rename from packages/create-sitecore-jss/src/initializers/nextjs-personalize/index.ts rename to packages/create-sitecore-jss/src/initializers/nextjs-xmcloud/index.ts index 6439b7b8e1..16813e229a 100644 --- a/packages/create-sitecore-jss/src/initializers/nextjs-personalize/index.ts +++ b/packages/create-sitecore-jss/src/initializers/nextjs-xmcloud/index.ts @@ -8,7 +8,7 @@ import { incompatibleAddonsMsg, } from '../../common'; -export default class NextjsPersonalizeInitializer implements Initializer { +export default class NextjsXMCloudInitializer implements Initializer { get isBase(): boolean { return false; } @@ -16,16 +16,13 @@ export default class NextjsPersonalizeInitializer implements Initializer { async init(args: ClientAppArgs) { const pkg = openPackageJson(`${args.destination}${sep}package.json`); - // TODO: prompts for Personalize and argument types - // const answers = await prompt(styleguidePrompts, args); - const mergedArgs = { ...args, appName: args.appName || pkg?.config?.appName || DEFAULT_APPNAME, appPrefix: args.appPrefix || pkg?.config?.prefix || false, }; - const templatePath = path.resolve(__dirname, '../../templates/nextjs-personalize'); + const templatePath = path.resolve(__dirname, '../../templates/nextjs-xmcloud'); await transform(templatePath, mergedArgs); @@ -33,7 +30,7 @@ export default class NextjsPersonalizeInitializer implements Initializer { args.templates.includes('nextjs-styleguide-tracking') || pkg.config?.templates?.includes('nextjs-styleguide-tracking') ) { - console.log(incompatibleAddonsMsg('nextjs-personalize', 'nextjs-styleguide-tracking')); + console.log(incompatibleAddonsMsg('nextjs-xmcloud', 'nextjs-styleguide-tracking')); } const response = { diff --git a/packages/create-sitecore-jss/src/initializers/nextjs/index.ts b/packages/create-sitecore-jss/src/initializers/nextjs/index.ts index 843dec78a6..ad9fe98826 100644 --- a/packages/create-sitecore-jss/src/initializers/nextjs/index.ts +++ b/packages/create-sitecore-jss/src/initializers/nextjs/index.ts @@ -14,6 +14,36 @@ import { NextjsArgs } from './args'; inquirer.registerPrompt('nextjs-checkbox', NextjsCheckbox); +enum PlatformCompatibility { + SXP, + XMC, + Both, +} + +const addOnChoices = [ + { + name: 'nextjs-styleguide - Includes example components and setup for working disconnected', + value: 'nextjs-styleguide', + platform: PlatformCompatibility.Both, + }, + { + name: 'nextjs-styleguide-tracking - Includes example (Sitecore XP) tracking component', + value: 'nextjs-styleguide-tracking', + platform: PlatformCompatibility.SXP, + }, + { + name: 'nextjs-sxa - Includes example components and setup for Headless SXA projects', + value: 'nextjs-sxa', + platform: PlatformCompatibility.Both, + }, + { + name: + 'nextjs-multisite - Includes example setup for hosting multiple sites in a single NextJS application', + value: 'nextjs-multisite', + platform: PlatformCompatibility.Both, + }, +]; + export default class NextjsInitializer implements Initializer { get isBase(): boolean { return true; @@ -31,7 +61,12 @@ export default class NextjsInitializer implements Initializer { removeDevDependencies(args.destination); } - let addInitializers: string[] = []; + const addInitializers: string[] = []; + + if (answers.xmcloud && !args.templates.includes('nextjs-xmcloud')) { + // add the "system" nextjs-xmcloud template if needed + addInitializers.push('nextjs-xmcloud'); + } // don't prompt for add-on initializers if --yes or they've already specified // multiple via --templates (assume they know what they're doing) @@ -40,33 +75,15 @@ export default class NextjsInitializer implements Initializer { type: 'nextjs-checkbox' as 'checkbox', name: 'addInitializers', message: 'Would you like to include any add-on initializers?', - choices: [ - { - name: - 'nextjs-styleguide - Includes example components and setup for working disconnected', - value: 'nextjs-styleguide', - }, - { - name: 'nextjs-styleguide-tracking - Includes example (Sitecore XP) tracking component', - value: 'nextjs-styleguide-tracking', - }, - { - name: 'nextjs-sxa - Includes example components and setup for Headless SXA projects', - value: 'nextjs-sxa', - }, - { - name: - 'nextjs-personalize - Includes example setup for projects using XM Cloud Embedded Personalization', - value: 'nextjs-personalize', - }, - { - name: - 'nextjs-multisite - Includes example setup for hosting multiple sites in a single NextJS application', - value: 'nextjs-multisite', - }, - ], + choices: addOnChoices.filter((choice) => { + return ( + choice.platform === PlatformCompatibility.Both || + (answers.xmcloud && choice.platform === PlatformCompatibility.XMC) || + (!answers.xmcloud && choice.platform === PlatformCompatibility.SXP) + ); + }), }); - addInitializers = addInitAnswer.addInitializers; + addInitializers.push(...addInitAnswer.addInitializers); } if ( diff --git a/packages/create-sitecore-jss/src/initializers/nextjs/prompts.ts b/packages/create-sitecore-jss/src/initializers/nextjs/prompts.ts index e501ed1d91..1c04472e20 100644 --- a/packages/create-sitecore-jss/src/initializers/nextjs/prompts.ts +++ b/packages/create-sitecore-jss/src/initializers/nextjs/prompts.ts @@ -10,6 +10,7 @@ export enum Prerender { export type NextjsAnswer = ClientAppAnswer & { prerender: Prerender; + xmcloud: boolean; }; const DEFAULT_PRERENDER = Prerender.SSG; @@ -29,6 +30,15 @@ export const prompts: QuestionCollection = [ return !answers.prerender; }, }, + { + type: 'confirm', + name: 'xmcloud', + message: 'Are you building for Sitecore XM Cloud?', + default: false, + when: (answers: NextjsAnswer): boolean => { + return !answers.yes; + }, + }, ]; /** @@ -45,15 +55,6 @@ export class NextjsCheckbox extends CheckboxPrompt { return value === initializer && checked; }); - const isPersonalizeSelected = isSelected('nextjs-personalize'); - const isTrackingSelected = isSelected('nextjs-styleguide-tracking'); - - if (isPersonalizeSelected && isTrackingSelected) { - this.onError({ - isValid: incompatibleAddonsMsg('nextjs-styleguide-tracking', 'nextjs-personalize'), - }); - } - const isSxaSelected = isSelected('nextjs-sxa'); const isStyleguideSelected = isSelected('nextjs-styleguide'); From 1e8f7b46a0370f50ab86390fcb022882ee4815ad Mon Sep 17 00:00:00 2001 From: Adam Brauer <400763+ambrauer@users.noreply.github.com> Date: Mon, 6 Nov 2023 16:16:41 -0600 Subject: [PATCH 14/37] Moved skipping of site information fetch on XM Cloud to base package (GraphQLSiteInfoService) --- .../scripts/config/plugins/multisite.ts | 23 +++++------- .../src/site/graphql-siteinfo-service.test.ts | 36 ++++++++++++++++++- .../src/site/graphql-siteinfo-service.ts | 4 +++ 3 files changed, 48 insertions(+), 15 deletions(-) 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 dce37363b5..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 @@ -14,20 +14,15 @@ class MultisitePlugin implements ConfigPlugin { async exec(config: JssConfig) { let sites: SiteInfo[] = []; - - if (process.env.SITECORE) { - console.warn(chalk.yellow('Skipping site information fetch (building on XM Cloud)')); - } else { - console.log('Fetching site information'); - try { - const siteInfoService = new GraphQLSiteInfoService({ - clientFactory: createGraphQLClientFactory(config), - }); - sites = await siteInfoService.fetchSiteInfo(); - } catch (error) { - console.error(chalk.red('Error fetching site information')); - console.error(error); - } + console.log('Fetching site information'); + try { + const siteInfoService = new GraphQLSiteInfoService({ + clientFactory: createGraphQLClientFactory(config), + }); + sites = await siteInfoService.fetchSiteInfo(); + } catch (error) { + console.error(chalk.red('Error fetching site information')); + console.error(error); } return Object.assign({}, config, { 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 d9c95db7d4..53f61b98a5 100644 --- a/packages/sitecore-jss/src/site/graphql-siteinfo-service.test.ts +++ b/packages/sitecore-jss/src/site/graphql-siteinfo-service.test.ts @@ -1,10 +1,17 @@ +/* eslint-disable no-unused-expressions */ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { expect } from 'chai'; +import { expect, spy, use } from 'chai'; +import spies from 'chai-spies'; import nock from 'nock'; import { GraphQLSiteInfoService, GraphQLSiteInfoResult } from './graphql-siteinfo-service'; import { GraphQLRequestClient, PageInfo } from '../graphql'; +import debugApi from 'debug'; +import debug from '../debug'; + +use(spies); describe('GraphQLSiteInfoService', () => { + let debugNamespaces: string; const endpoint = 'http://site'; const apiKey = 'some-api-key'; @@ -63,8 +70,23 @@ describe('GraphQLSiteInfoService', () => { }, }; + before(() => { + debugNamespaces = debugApi.disable(); + debugApi.enable(debug.multisite.namespace); + }); + + beforeEach(() => { + spy.on(debug.multisite, 'log', () => true); + }); + afterEach(() => { nock.cleanAll(); + spy.restore(debug.multisite); + delete process.env.SITECORE; + }); + + after(() => { + debugApi.enable(debugNamespaces); }); const mockSiteInfoRequest = (response: { [key: string]: unknown }) => { @@ -269,4 +291,16 @@ describe('GraphQLSiteInfoService', () => { const resultCached = await service.fetchSiteInfo(); expect(resultCached).to.deep.equal([]); }); + + it('should skip on XM Cloud', async () => { + process.env.SITECORE = 'true'; + nock(endpoint) + .post('/') + .reply(200, emptyResponse); + const service = new GraphQLSiteInfoService({ apiKey: apiKey, endpoint: endpoint }); + const result = await service.fetchSiteInfo(); + expect(result).to.deep.equal([]); + expect(debug.multisite.log, 'log debug message').to.be.called.once; + expect(nock.isDone(), 'skip request').to.be.false; + }); }); diff --git a/packages/sitecore-jss/src/site/graphql-siteinfo-service.ts b/packages/sitecore-jss/src/site/graphql-siteinfo-service.ts index 8c57ca256c..c0b87758f4 100644 --- a/packages/sitecore-jss/src/site/graphql-siteinfo-service.ts +++ b/packages/sitecore-jss/src/site/graphql-siteinfo-service.ts @@ -134,6 +134,10 @@ export class GraphQLSiteInfoService { if (cachedResult) { return cachedResult; } + if (process.env.SITECORE) { + debug.multisite('Skipping site information fetch (building on XM Cloud)'); + return []; + } const results: SiteInfo[] = []; let hasNext = true; From bf351617d93b74ffbca3d0de80e2a006335ead87 Mon Sep 17 00:00:00 2001 From: Adam Brauer <400763+ambrauer@users.noreply.github.com> Date: Mon, 6 Nov 2023 16:54:17 -0600 Subject: [PATCH 15/37] CHANGELOG update --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20382448f7..50df2c7172 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ Our versioning strategy is as follows: * `[sitecore-jss-nextjs]` (Vercel/Sitecore) Deployment Protection Bypass support for editing integration. ([#1634](https://github.com/Sitecore/jss/pull/1634)) * `[sitecore-jss]` `[templates/nextjs]` Load environment-specific FEAAS theme stylesheets based on Sitecore Edge Platform URL ([#1645](https://github.com/Sitecore/jss/pull/1645)) * `[sitecore-jss-nextjs]` The _GraphQLRequestClient_ import from _@sitecore-jss/sitecore-jss-nextjs_ is deprecated, use import from _@sitecore-jss/sitecore-jss-nextjs/graphql_ submodule instead ([#1650](https://github.com/Sitecore/jss/pull/1650) [#1648](https://github.com/Sitecore/jss/pull/1648)) +* `[create-sitecore-jss]` Introduced `nextjs-xmcloud` initializer template. This will include all base XM Cloud features, including Personalize, FEaaS, BYOC, Sitecore Edge Platform and Context support. ([#1653](https://github.com/Sitecore/jss/pull/1653)) ### 🐛 Bug Fixes @@ -33,7 +34,11 @@ Our versioning strategy is as follows: * `[sitecore-jss-nextjs]` Fix issue when redirects works each every other times. ([#1629](https://github.com/Sitecore/jss/pull/1629)) * `[templates/nextjs]` Fix custom headers. Now in cors-header plugin extends custom headers from next.config.js file. ([#1637](https://github.com/Sitecore/jss/pull/1637)) * `[sitecore-jss-react]` Fix PlaceholderCommon with using two and more dynamic placeholders. ([#1641](https://github.com/Sitecore/jss/pull/1641)) -* `[templates/nextjs-multisite]` Fix site info fetch errors (now skipped) on XM Cloud rendering/editing host builds. ([#1649](https://github.com/Sitecore/jss/pull/1649)) +* `[templates/nextjs-multisite]` Fix site info fetch errors (now skipped) on XM Cloud rendering/editing host builds. ([#1649](https://github.com/Sitecore/jss/pull/1649)), ([#1653](https://github.com/Sitecore/jss/pull/1653)) + +### 🛠 Breaking Changes + +* `[create-sitecore-jss]` The `nextjs-personalize` initializer add-on template has been removed and is replaced by the `nextjs-xmcloud` initializer template. You can use the interactive prompts or the `--xmcloud` argument to include this template. ([#1653](https://github.com/Sitecore/jss/pull/1653)) ## 21.5.0 From b79b198c8085ff5c95637d6f4551c66f7604e19f Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 7 Nov 2023 14:26:16 +0200 Subject: [PATCH 16/37] Updated Context implementation, added unit tests --- .../src/components/CdpPageView.tsx | 12 +- .../src/lib/context/events.ts | 18 --- .../src/lib/context/plugins/events.ts | 31 ---- .../src/lib/context/sdk/events.ts | 26 ++++ .../nextjs/scripts/generate-plugins.ts | 5 - .../templates/nextjs/src/lib/context/index.ts | 94 ++++++------ .../nextjs/src/pages/[[...path]].tsx | 3 +- packages/sitecore-jss-nextjs/context.d.ts | 1 + packages/sitecore-jss-nextjs/context.js | 1 + packages/sitecore-jss-nextjs/package.json | 2 +- .../src/context/context.test.ts | 145 ++++++++++++++++++ .../src/context/context.ts | 100 ++++++++++++ .../sitecore-jss-nextjs/src/context/index.ts | 1 + packages/sitecore-jss-nextjs/src/index.ts | 2 + packages/sitecore-jss-nextjs/tsconfig.json | 1 + 15 files changed, 333 insertions(+), 109 deletions(-) delete mode 100644 packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/events.ts delete mode 100644 packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts create mode 100644 packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/sdk/events.ts create mode 100644 packages/sitecore-jss-nextjs/context.d.ts create mode 100644 packages/sitecore-jss-nextjs/context.js create mode 100644 packages/sitecore-jss-nextjs/src/context/context.test.ts create mode 100644 packages/sitecore-jss-nextjs/src/context/context.ts create mode 100644 packages/sitecore-jss-nextjs/src/context/index.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/components/CdpPageView.tsx b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/components/CdpPageView.tsx index 2fe3c0e19c..d0d6d53f49 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/components/CdpPageView.tsx +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/components/CdpPageView.tsx @@ -5,7 +5,7 @@ import { } from '@sitecore-jss/sitecore-jss-nextjs'; import { useEffect } from 'react'; import config from 'temp/config'; -import { createPageView } from 'lib/context/events'; +import { context } from 'lib/context'; /** * This is the CDP page view component. @@ -47,7 +47,15 @@ const CdpPageView = (): JSX.Element => { scope ); - createPageView(route.name, language, pageVariantId); + context.getSDK('Events')?.then((Events) => + Events.pageView({ + channel: 'WEB', + currency: 'USD', + page: route.name, + pageVariantId, + language, + }) + ); }, [pageState, route, variantId, site]); return <>; diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/events.ts b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/events.ts deleted file mode 100644 index 9ad5f5c279..0000000000 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/events.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as EventsSDK from '@sitecore-cloudsdk/events/browser'; -import { whenSDKReady } from './'; - -/** - * Creates a page view event using the Sitecore Cloud SDK Events. - * This function is used to trigger a pageView event. The event will be sent once the SDK is initialized. - */ -export const createPageView = async (page: string, language: string, pageVariantId: string) => { - return whenSDKReady('Events').then((Events) => - Events.pageView({ - channel: 'WEB', - currency: 'USD', - page, - pageVariantId, - language, - }) - ); -}; diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts deleted file mode 100644 index c9075abb48..0000000000 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/plugins/events.ts +++ /dev/null @@ -1,31 +0,0 @@ -import * as Events from '@sitecore-cloudsdk/events/browser'; -import { Plugin, Props, Context } from '..'; - -/** - * This is Events plugin for Context initialization. - * It is used to enable Cloud SDK Events in Next.js. - */ -class EventsPlugin implements Plugin { - order = 0; - - SDK = { - name: 'Events', - lib: Events, - }; - - async exec(props: Props, context: Context) { - // Events module can't be initialized on the server side - if (typeof window === 'undefined') return; - - await Events.init({ - siteName: props.site, - sitecoreEdgeContextId: context.sitecoreEdgeContextId, - // Replace with the top level cookie domain of the website that is being integrated e.g ".example.com" and not "www.example.com" - cookieDomain: window.location.hostname.replace(/^www\./, ''), - // Cookie may be created in personalize middleware (server), but if not we should create it here - enableBrowserCookie: true, - }); - } -} - -export const eventsPlugin = new EventsPlugin(); diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/sdk/events.ts b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/sdk/events.ts new file mode 100644 index 0000000000..1cadafb04c --- /dev/null +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/sdk/events.ts @@ -0,0 +1,26 @@ +import * as Events from '@sitecore-cloudsdk/events/browser'; +import { contextState, SDK } from '../index'; + +const sdk = Events; + +const init = async () => { + // Events module can't be initialized on the server side + // We also don't want to initialize it in development mode + if (typeof window === 'undefined' || process.env.NODE_ENV === 'development') return; + + return Events.init({ + siteName: contextState.siteName, + sitecoreEdgeContextId: contextState.sitecoreEdgeContextId, + // Replace with the top level cookie domain of the website that is being integrated e.g ".example.com" and not "www.example.com" + cookieDomain: window.location.hostname.replace(/^www\./, ''), + // Cookie may be created in personalize middleware (server), but if not we should create it here + enableBrowserCookie: true, + }); +}; + +const sdkModule: SDK = { + sdk, + init, +}; + +export default sdkModule; diff --git a/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-plugins.ts b/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-plugins.ts index ec470c98ba..46e59e7a8a 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-plugins.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs/scripts/generate-plugins.ts @@ -57,11 +57,6 @@ const pluginDefinitions: PluginDefinition[] = [ rootPath: 'src/lib/site-resolver/plugins', moduleType: ModuleType.ESM, }, - { - distPath: 'src/temp/context-plugins.ts', - rootPath: 'src/lib/context/plugins', - moduleType: ModuleType.ESM, - }, ]; pluginDefinitions.forEach(definition => { diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts index 322e3eefc7..946c93b209 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts @@ -1,57 +1,47 @@ import * as FEAAS from '@sitecore-feaas/clientside/react'; -import * as plugins from 'temp/context-plugins'; +import { Context } from '@sitecore-jss/sitecore-jss-nextjs/context'; import config from 'temp/config'; +import Events from './sdk/events'; + +// Shape of the SDK object +export type SDK = { + sdk: SDKType; + init: () => Promise; +}; + +/** + * List of SDKs to be initialized. + * Each SDK is defined as a module with the @type {SDK} type. + */ +const modules = { + Events, +}; + +type SDKName = keyof typeof modules; + +// SDKs that are initialized by the Context +type SDKs = { [name in SDKName]: typeof modules[name]['sdk'] }; + /** * Plugin's incoming properties */ export interface Props { - site: string; + siteName: string; } /** - * Context definition + * Context instance that is used to initialize the application Context and associated Software Development Kits (SDKs). */ -export interface Context { - sitecoreEdgeUrl: string; - sitecoreEdgeContextId: string; - SDK: { [key: string]: unknown }; -} - -export interface Plugin { - /** - * Detect order when the plugin should be called, e.g. 0 - will be called first (can be a plugin which has to be initialized before other plugins) - */ - order: number; - /** - * A function to be called during SDK initialization. - */ - exec(props: Props, context: Context): Promise; - /** - * The SDK associated with the plugin. - */ - SDK: { - name: string; - lib: unknown; - }; -} - -export const context: Context = { +export const context = new Context({ sitecoreEdgeUrl: config.sitecoreEdgeUrl, sitecoreEdgeContextId: config.sitecoreEdgeContextId, - SDK: {}, -}; - -// Initialize promise list -export const promises: { [key: string]: Promise } = {}; - -// Helper to wait for asynchronosuly initialized SDK -export function whenSDKReady(name: string): Promise { - return promises[name] as Promise; -} +}); -// Indicates whether the Context and SDK(s) have been initialized. -let initialized = false; +/** + * The application Context State + */ +export let contextState = context.getState(); /** * Initializes the application Context and associated Software Development Kits (SDKs). @@ -59,23 +49,25 @@ let initialized = false; * It prepares the resources needed to interact with various services and features within the application. */ export const initContext = async (props: Props) => { - if (initialized) return; + // Context and SDKs are initialized only once + if (context.isInitialized) return; - initialized = true; + context.isInitialized = true; - const pluginList = Object.values(plugins).sort((p1, p2) => p1.order - p2.order) as Plugin[]; + // Updating the context with the incoming properties + contextState = context.getState({ + siteName: props.siteName, + }); - for (const plugin of pluginList) { - promises[plugin.SDK.name] = new Promise(async (resolve) => { - await plugin.exec(props, context); + // iterate over the SDKs and initialize them + for (const sdkName of Object.keys(modules) as SDKName[]) { + await context.initSDK(sdkName, async () => { + await modules[sdkName].init(); - context.SDK[plugin.SDK.name] = plugin.SDK.lib; - - resolve(plugin.SDK.lib); + return modules[sdkName].sdk; }); - - await promises[plugin.SDK.name]; } + // Setting the context properties for the FEAAS SDK FEAAS.setContextProperties(context); }; diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/pages/[[...path]].tsx b/packages/create-sitecore-jss/src/templates/nextjs/src/pages/[[...path]].tsx index da1c1fb203..094d525a6b 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/pages/[[...path]].tsx +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/pages/[[...path]].tsx @@ -41,7 +41,8 @@ const SitecorePage = ({ notFound, componentProps, layoutData, headLinks }: Sitec const site = layoutData.sitecore.context.site; const siteInfo = siteResolver.getByName(site?.name || config.siteName); - initContext({ site: siteInfo.name }); + // Initialize the Context of the App + initContext({ siteName: siteInfo.name }); const isEditing = layoutData.sitecore.context.pageEditing; const isComponentRendering = diff --git a/packages/sitecore-jss-nextjs/context.d.ts b/packages/sitecore-jss-nextjs/context.d.ts new file mode 100644 index 0000000000..72e501678d --- /dev/null +++ b/packages/sitecore-jss-nextjs/context.d.ts @@ -0,0 +1 @@ +export * from './types/context/index'; diff --git a/packages/sitecore-jss-nextjs/context.js b/packages/sitecore-jss-nextjs/context.js new file mode 100644 index 0000000000..e9529d5720 --- /dev/null +++ b/packages/sitecore-jss-nextjs/context.js @@ -0,0 +1 @@ +module.exports = require('./dist/cjs/context/index'); diff --git a/packages/sitecore-jss-nextjs/package.json b/packages/sitecore-jss-nextjs/package.json index 51f876f8a9..413cad7e85 100644 --- a/packages/sitecore-jss-nextjs/package.json +++ b/packages/sitecore-jss-nextjs/package.json @@ -11,7 +11,7 @@ "test": "mocha --require ./test/setup.js \"./src/**/*.test.ts\" \"./src/**/*.test.tsx\" --exit", "prepublishOnly": "npm run build", "coverage": "nyc npm test", - "generate-docs": "npx typedoc --plugin typedoc-plugin-markdown --readme none --out ../../ref-docs/sitecore-jss-nextjs --entryPoints src/index.ts --entryPoints src/monitoring/index.ts --entryPoints src/editing/index.ts --entryPoints src/middleware/index.ts --githubPages false" + "generate-docs": "npx typedoc --plugin typedoc-plugin-markdown --readme none --out ../../ref-docs/sitecore-jss-nextjs --entryPoints src/index.ts --entryPoints src/monitoring/index.ts --entryPoints src/editing/index.ts --entryPoints src/middleware/index.ts --entryPoints src/context/index.ts --githubPages false" }, "engines": { "node": ">=12", diff --git a/packages/sitecore-jss-nextjs/src/context/context.test.ts b/packages/sitecore-jss-nextjs/src/context/context.test.ts new file mode 100644 index 0000000000..41c51db757 --- /dev/null +++ b/packages/sitecore-jss-nextjs/src/context/context.test.ts @@ -0,0 +1,145 @@ +/* eslint-disable no-unused-expressions */ +/* eslint-disable dot-notation */ +import sinon from 'sinon'; +import { expect } from 'chai'; +import { Context } from './'; + +describe('Context', () => { + const props = { + sitecoreEdgeUrl: 'https://edgeurl', + sitecoreEdgeContextId: 'contextid', + }; + + const sdks = { + Foo: { + sdk: { foo: true }, + init: sinon.stub().callsFake(() => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(sdks.Foo.sdk); + }, 300); + }); + }), + }, + Bar: { + sdk: { bar: true }, + create: sinon.stub().callsFake(() => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(sdks.Bar.sdk); + }, 500); + }); + }), + }, + }; + + const initFoo = async () => { + await sdks.Foo.init(); + + return sdks.Foo.sdk; + }; + + const initBar = async () => { + await sdks.Bar.create(); + + return sdks.Bar.sdk; + }; + + type SDKName = keyof typeof sdks; + + type SDKs = { + [name in SDKName]: typeof sdks[name]['sdk']; + }; + + afterEach(() => { + sdks.Bar.create.reset(); + sdks.Foo.init.reset(); + }); + + describe('initSDK', () => { + it('should initialize the SDKs', async () => { + const context = new Context(props); + + await context.initSDK('Foo', initFoo); + + await context.initSDK('Bar', initBar); + + expect(context['SDK'].Foo).to.deep.equal(sdks.Foo.sdk); + expect(context['SDK'].Bar).to.deep.equal(sdks.Bar.sdk); + + expect(sdks.Foo.init.calledOnce).to.be.true; + expect(sdks.Bar.create.calledOnce).to.be.true; + }); + }); + + describe('getSDK', () => { + it('should return the SDKs', (done) => { + const context = new Context(props); + + context.initSDK('Foo', initFoo); + context.initSDK('Bar', initBar); + + Promise.all([ + context.getSDK('Foo')?.then((sdk) => { + expect(sdks.Foo.init.calledOnce).to.be.true; + expect(sdk).to.deep.equal(sdks.Foo.sdk); + + return; + }), + context.getSDK('Bar')?.then((sdk) => { + expect(sdks.Bar.create.calledOnce).to.be.true; + expect(sdk).to.deep.equal(sdks.Bar.sdk); + + return; + }), + ]).then(() => { + expect(context['SDK'].Foo).to.deep.equal(sdks.Foo.sdk); + expect(context['SDK'].Bar).to.deep.equal(sdks.Bar.sdk); + + done(); + }); + }); + }); + + describe('getState', () => { + it('should return the context state', () => { + const context = new Context(props); + + expect(context.getState({ siteName: 'xyz' })).to.deep.equal({ + sitecoreEdgeUrl: props.sitecoreEdgeUrl, + sitecoreEdgeContextId: props.sitecoreEdgeContextId, + SDK: {}, + siteName: 'xyz', + }); + }); + + it('should return the context state when siteName is not provided', () => { + const context = new Context(props); + + expect(context.getState()).to.deep.equal({ + sitecoreEdgeUrl: props.sitecoreEdgeUrl, + sitecoreEdgeContextId: props.sitecoreEdgeContextId, + SDK: {}, + siteName: '', + }); + }); + + it('should return the context state with SDKs', async () => { + const context = new Context(props); + + await context.initSDK('Foo', initFoo); + + await context.initSDK('Bar', initBar); + + expect(context.getState()).to.deep.equal({ + sitecoreEdgeUrl: props.sitecoreEdgeUrl, + sitecoreEdgeContextId: props.sitecoreEdgeContextId, + SDK: { + Foo: sdks.Foo.sdk, + Bar: sdks.Bar.sdk, + }, + siteName: '', + }); + }); + }); +}); diff --git a/packages/sitecore-jss-nextjs/src/context/context.ts b/packages/sitecore-jss-nextjs/src/context/context.ts new file mode 100644 index 0000000000..4c5bbcb86a --- /dev/null +++ b/packages/sitecore-jss-nextjs/src/context/context.ts @@ -0,0 +1,100 @@ +/** + * Context State that keeps track of the Context and Software Development Kits (SDKs) + */ +export interface ContextState { + /** + * Your Sitecore Edge URL + */ + sitecoreEdgeUrl: string; + /** + * Your Sitecore Edge Context ID + */ + sitecoreEdgeContextId: string; + /** + * Software Development Kits (SDKs) that are initialized by the ContextInitializer + */ + SDK: { [module in keyof SDKModules]?: SDKModules[module] }; + /** + * Site name + */ + siteName: string; +} + +// Configuration for the Context +export interface ContextConfig { + /** + * Your Sitecore Edge URL + */ + sitecoreEdgeUrl: string; + /** + * Your Sitecore Edge Context ID + */ + sitecoreEdgeContextId: string; +} + +/** + * Context instance that is used to initialize the application Context and associated Software Development Kits (SDKs). + */ +export class Context { + /** + * Indicates whether the Context and SDK(s) have been initialized + */ + public isInitialized = false; + + /** + * Software Development Kits (SDKs) to be initialized + */ + protected SDK: { [module in keyof SDKModules]?: SDKModules[module] } = {}; + + /** + * Promises for the SDKs + */ + protected promises: { [module in keyof SDKModules]?: Promise } = {}; + + constructor(protected props: ContextConfig) {} + + /** + * Retrieves the context object. + * + * @param {Object} params incoming parameters + * @param {string} params.siteName Site name + * @returns context object + */ + public getState({ siteName = '' }: { siteName?: string } = {}): ContextState { + return { + sitecoreEdgeUrl: this.props.sitecoreEdgeUrl, + sitecoreEdgeContextId: this.props.sitecoreEdgeContextId, + SDK: this.SDK, + siteName, + }; + } + + /** + * Initializes the Software Development Kit (SDK). + * This function is the entry point for setting up the SDK. + * + * @param {string} name SDK name + * @param {Function} cb Callback function that initializes the SDK + */ + public async initSDK(name: T, cb: () => Promise) { + this.promises[name] = new Promise((resolve) => { + cb().then((sdk) => { + this.SDK[name] = sdk; + + resolve(sdk); + }); + }); + + await this.promises[name]; + } + + /** + * Retrieves the SDK instance, ensuring it is initialized before returning + * + * @param {string} name SDK name + * @returns initialized SDK + */ + public getSDK(name: T): Promise | undefined { + return this.promises[name]; + } +} diff --git a/packages/sitecore-jss-nextjs/src/context/index.ts b/packages/sitecore-jss-nextjs/src/context/index.ts new file mode 100644 index 0000000000..a70da45fc7 --- /dev/null +++ b/packages/sitecore-jss-nextjs/src/context/index.ts @@ -0,0 +1 @@ +export { Context, ContextConfig, ContextState } from './context'; diff --git a/packages/sitecore-jss-nextjs/src/index.ts b/packages/sitecore-jss-nextjs/src/index.ts index 444bf2d301..b64dd719db 100644 --- a/packages/sitecore-jss-nextjs/src/index.ts +++ b/packages/sitecore-jss-nextjs/src/index.ts @@ -165,6 +165,8 @@ export { BYOCWrapper }; export { ComponentBuilder, ComponentBuilderConfig } from './ComponentBuilder'; +export { Context, ContextConfig, ContextState } from './context'; + export { ComponentFactory, Image, diff --git a/packages/sitecore-jss-nextjs/tsconfig.json b/packages/sitecore-jss-nextjs/tsconfig.json index 348881666f..9d2cf9eaff 100644 --- a/packages/sitecore-jss-nextjs/tsconfig.json +++ b/packages/sitecore-jss-nextjs/tsconfig.json @@ -14,6 +14,7 @@ "types", "typings", "dist", + "context.d.ts", "middleware.d.ts", "editing.d.ts", "monitoring.d.ts", From 22deb3e09c33ccb05c8c79cee26b7f3a879a65f5 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 7 Nov 2023 14:29:17 +0200 Subject: [PATCH 17/37] Updated CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e830b5e85..f6454c4c4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ Our versioning strategy is as follows: * `[templates/nextjs]` `[sitecore-jss-nextjs]` CloudSDK Integration ([#1652](https://github.com/Sitecore/jss/pull/1652)): * Removed the following properties from _PersonalizeMiddleware_: _getPointOfSale_, _clientKey_, _endpoint_. You now need to provide _sitecoreEdgeContextId_ as a replacement. * _PersonalizeMiddleware_ has transitioned to utilizing the _CloudSDK_ package, replacing the previous dependency on _Engage_. + * Introduced _Context_ class, that is used to initialize the application Context and associated Software Development Kits (SDKs). Accessible within the _@sitecore-jss/sitecore-jss-nextjs/context_ submodule. ## 21.5.0 From 6349af7fe26dbaee062189b5c6cf69d3c15e5cc7 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 7 Nov 2023 16:25:17 +0200 Subject: [PATCH 18/37] Updated comment --- packages/sitecore-jss-nextjs/src/context/context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sitecore-jss-nextjs/src/context/context.ts b/packages/sitecore-jss-nextjs/src/context/context.ts index 4c5bbcb86a..50eb55e8e9 100644 --- a/packages/sitecore-jss-nextjs/src/context/context.ts +++ b/packages/sitecore-jss-nextjs/src/context/context.ts @@ -89,7 +89,7 @@ export class Context { } /** - * Retrieves the SDK instance, ensuring it is initialized before returning + * Retrieves the Software Development Kit (SDK) instance, ensuring it is initialized before returning * * @param {string} name SDK name * @returns initialized SDK From 49af700a8d940063fdebbf0944ba362c668a3b87 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 7 Nov 2023 16:36:48 +0200 Subject: [PATCH 19/37] Updated cloudsdk to use latest production version --- .../src/templates/nextjs-personalize/package.json | 2 +- packages/sitecore-jss-nextjs/package.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/package.json b/packages/create-sitecore-jss/src/templates/nextjs-personalize/package.json index 3d38508632..f7abebb17c 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-personalize/package.json +++ b/packages/create-sitecore-jss/src/templates/nextjs-personalize/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "@sitecore-cloudsdk/events": "^0.1.0-rc.4" + "@sitecore-cloudsdk/events": "^0.1.1" } } diff --git a/packages/sitecore-jss-nextjs/package.json b/packages/sitecore-jss-nextjs/package.json index 413cad7e85..d4a07ee15a 100644 --- a/packages/sitecore-jss-nextjs/package.json +++ b/packages/sitecore-jss-nextjs/package.json @@ -30,7 +30,7 @@ "url": "https://github.com/sitecore/jss/issues" }, "devDependencies": { - "@sitecore-cloudsdk/personalize": "^0.1.0-rc.4", + "@sitecore-cloudsdk/personalize": "^0.1.1", "@types/chai": "^4.3.4", "@types/chai-as-promised": "^7.1.5", "@types/chai-string": "^1.4.2", @@ -66,8 +66,8 @@ "typescript": "~4.9.4" }, "peerDependencies": { - "@sitecore-cloudsdk/events": "^0.1.0-rc.4", - "@sitecore-cloudsdk/personalize": "^0.1.0-rc.4", + "@sitecore-cloudsdk/events": "^0.1.1", + "@sitecore-cloudsdk/personalize": "^0.1.1", "next": "^13.4.16", "react": "^18.2.0", "react-dom": "^18.2.0" From 9e8d7f56a2118c8cc1285259bc9235dd6749dcc6 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 7 Nov 2023 16:38:00 +0200 Subject: [PATCH 20/37] Updated yarn.lock --- yarn.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/yarn.lock b/yarn.lock index 724813203d..1c1c678f64 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6442,13 +6442,13 @@ __metadata: languageName: node linkType: hard -"@sitecore-cloudsdk/personalize@npm:^0.1.0-rc.4": - version: 0.1.0-rc.4 - resolution: "@sitecore-cloudsdk/personalize@npm:0.1.0-rc.4" +"@sitecore-cloudsdk/personalize@npm:^0.1.1": + version: 0.1.1 + resolution: "@sitecore-cloudsdk/personalize@npm:0.1.1" dependencies: "@sitecore-cloudsdk/core": ^0.1.0-rc.2 "@sitecore-cloudsdk/utils": ^0.1.0-rc.2 - checksum: 87623064f786234dfc03072d695d988059f65327d3371d9280e03d904228a0f75733c0bf109dce6de0d9a51a49ce0ee9feee21c22b757e0ae04747267897f4aa + checksum: e32a4f4b416a39c7657e475c49b6793ecc376a961e318cf3065ef148f78d6c00207ce2364d492a43ec5729322450a5faab0082ce3dafd580af4c26df9212530f languageName: node linkType: hard @@ -6646,7 +6646,7 @@ __metadata: version: 0.0.0-use.local resolution: "@sitecore-jss/sitecore-jss-nextjs@workspace:packages/sitecore-jss-nextjs" dependencies: - "@sitecore-cloudsdk/personalize": ^0.1.0-rc.4 + "@sitecore-cloudsdk/personalize": ^0.1.1 "@sitecore-jss/sitecore-jss": 21.7.0-canary.1 "@sitecore-jss/sitecore-jss-dev-tools": 21.7.0-canary.1 "@sitecore-jss/sitecore-jss-react": 21.7.0-canary.1 @@ -6689,8 +6689,8 @@ __metadata: ts-node: ^10.9.1 typescript: ~4.9.4 peerDependencies: - "@sitecore-cloudsdk/events": ^0.1.0-rc.4 - "@sitecore-cloudsdk/personalize": ^0.1.0-rc.4 + "@sitecore-cloudsdk/events": ^0.1.1 + "@sitecore-cloudsdk/personalize": ^0.1.1 next: ^13.4.16 react: ^18.2.0 react-dom: ^18.2.0 From 40933f012ec1a8afd76101b1cdef46c306d1e6cf Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 7 Nov 2023 18:34:25 +0200 Subject: [PATCH 21/37] Introduced Bootstrap and pulled nextjs-xmcloud --- CHANGELOG.md | 3 --- .../templates/nextjs-sxa/src/pages/_app.tsx | 18 ++++++++++++------ .../src/templates/nextjs-xmcloud/package.json | 2 +- .../templates/nextjs-xmcloud/src/Bootstrap.tsx | 16 ++++++++++++++++ .../src/lib/context/index.ts | 0 .../src/lib/context/sdk/events.ts | 0 .../src/templates/nextjs/src/Bootstrap.tsx | 7 +++++++ .../templates/nextjs/src/pages/[[...path]].tsx | 9 --------- .../src/templates/nextjs/src/pages/_app.tsx | 18 ++++++++++++------ 9 files changed, 48 insertions(+), 25 deletions(-) create mode 100644 packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/Bootstrap.tsx rename packages/create-sitecore-jss/src/templates/{nextjs => nextjs-xmcloud}/src/lib/context/index.ts (100%) rename packages/create-sitecore-jss/src/templates/{nextjs-personalize => nextjs-xmcloud}/src/lib/context/sdk/events.ts (100%) create mode 100644 packages/create-sitecore-jss/src/templates/nextjs/src/Bootstrap.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 466c1cb7df..d9fdd36eed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,9 +39,6 @@ Our versioning strategy is as follows: ### 🛠 Breaking Changes * `[create-sitecore-jss]` The `nextjs-personalize` initializer add-on template has been removed and is replaced by the `nextjs-xmcloud` initializer template. You can use the interactive prompts or the `--xmcloud` argument to include this template. ([#1653](https://github.com/Sitecore/jss/pull/1653)) - -### 🛠 Breaking Changes - * `[templates/nextjs]` `[sitecore-jss-nextjs]` CloudSDK Integration ([#1652](https://github.com/Sitecore/jss/pull/1652)): * Removed the following properties from _PersonalizeMiddleware_: _getPointOfSale_, _clientKey_, _endpoint_. You now need to provide _sitecoreEdgeContextId_ as a replacement. * _PersonalizeMiddleware_ has transitioned to utilizing the _CloudSDK_ package, replacing the previous dependency on _Engage_. diff --git a/packages/create-sitecore-jss/src/templates/nextjs-sxa/src/pages/_app.tsx b/packages/create-sitecore-jss/src/templates/nextjs-sxa/src/pages/_app.tsx index 7bbf99b96c..b4ab44a12f 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-sxa/src/pages/_app.tsx +++ b/packages/create-sitecore-jss/src/templates/nextjs-sxa/src/pages/_app.tsx @@ -1,6 +1,7 @@ import type { AppProps } from 'next/app'; import { I18nProvider } from 'next-localization'; import { SitecorePageProps } from 'lib/page-props'; +import Bootstrap from 'src/Bootstrap'; import 'assets/main.scss'; @@ -8,12 +9,17 @@ function App({ Component, pageProps }: AppProps): JSX.Element const { dictionary, ...rest } = pageProps; return ( - // Use the next-localization (w/ rosetta) library to provide our translation dictionary to the app. - // Note Next.js does not (currently) provide anything for translation, only i18n routing. - // If your app is not multilingual, next-localization and references to it can be removed. - - - + <> + + {/* + // Use the next-localization (w/ rosetta) library to provide our translation dictionary to the app. + // Note Next.js does not (currently) provide anything for translation, only i18n routing. + // If your app is not multilingual, next-localization and references to it can be removed. + */} + + + + ); } diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/package.json b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/package.json index 33d06644d2..78b5fb83de 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/package.json +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/package.json @@ -1,7 +1,7 @@ { "dependencies": { "@sitecore/components": "~1.0.19", - "@sitecore/engage": "^1.4.1", + "@sitecore-cloudsdk/events": "^0.1.1", "@sitecore-feaas/clientside": "^0.4.12" } } diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/Bootstrap.tsx b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/Bootstrap.tsx new file mode 100644 index 0000000000..0ab7a85687 --- /dev/null +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/Bootstrap.tsx @@ -0,0 +1,16 @@ +import { SitecorePageProps } from 'lib/page-props'; +import { initContext } from 'src/lib/context'; +import { siteResolver } from 'lib/site-resolver'; +import config from 'temp/config'; + +const Bootstrap = (props: SitecorePageProps): JSX.Element | null => { + const site = props.layoutData?.sitecore.context.site; + const siteInfo = siteResolver.getByName(site?.name || config.siteName); + + // Initialize the Context of the App + initContext({ siteName: siteInfo.name }); + + return null; +}; + +export default Bootstrap; diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts similarity index 100% rename from packages/create-sitecore-jss/src/templates/nextjs/src/lib/context/index.ts rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/sdk/events.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/sdk/events.ts similarity index 100% rename from packages/create-sitecore-jss/src/templates/nextjs-personalize/src/lib/context/sdk/events.ts rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/sdk/events.ts diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/Bootstrap.tsx b/packages/create-sitecore-jss/src/templates/nextjs/src/Bootstrap.tsx new file mode 100644 index 0000000000..e596c140ed --- /dev/null +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/Bootstrap.tsx @@ -0,0 +1,7 @@ +import { SitecorePageProps } from 'lib/page-props'; + +const Bootstrap = (_props: SitecorePageProps): JSX.Element | null => { + return null; +}; + +export default Bootstrap; diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/pages/[[...path]].tsx b/packages/create-sitecore-jss/src/templates/nextjs/src/pages/[[...path]].tsx index 094d525a6b..97e6a91de3 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/pages/[[...path]].tsx +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/pages/[[...path]].tsx @@ -19,9 +19,6 @@ import { handleEditorFastRefresh } from '@sitecore-jss/sitecore-jss-nextjs/utils import { SitecorePageProps } from 'lib/page-props'; import { sitecorePagePropsFactory } from 'lib/page-props-factory'; import { componentBuilder } from 'temp/componentBuilder'; -import { initContext } from 'src/lib/context'; -import { siteResolver } from 'lib/site-resolver'; -import config from 'temp/config'; <% if (prerender === 'SSG') { -%> import { sitemapFetcher } from 'lib/sitemap-fetcher'; @@ -38,12 +35,6 @@ const SitecorePage = ({ notFound, componentProps, layoutData, headLinks }: Sitec return ; } - const site = layoutData.sitecore.context.site; - const siteInfo = siteResolver.getByName(site?.name || config.siteName); - - // Initialize the Context of the App - initContext({ siteName: siteInfo.name }); - const isEditing = layoutData.sitecore.context.pageEditing; const isComponentRendering = layoutData.sitecore.context.renderingType === RenderingType.Component; diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/pages/_app.tsx b/packages/create-sitecore-jss/src/templates/nextjs/src/pages/_app.tsx index 7fef8a5794..37f4f24162 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/pages/_app.tsx +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/pages/_app.tsx @@ -1,6 +1,7 @@ import type { AppProps } from 'next/app'; import { I18nProvider } from 'next-localization'; import { SitecorePageProps } from 'lib/page-props'; +import Bootstrap from 'src/Bootstrap'; import 'assets/app.css'; @@ -8,12 +9,17 @@ function App({ Component, pageProps }: AppProps): JSX.Element const { dictionary, ...rest } = pageProps; return ( - // Use the next-localization (w/ rosetta) library to provide our translation dictionary to the app. - // Note Next.js does not (currently) provide anything for translation, only i18n routing. - // If your app is not multilingual, next-localization and references to it can be removed. - - - + <> + + {/* + // Use the next-localization (w/ rosetta) library to provide our translation dictionary to the app. + // Note Next.js does not (currently) provide anything for translation, only i18n routing. + // If your app is not multilingual, next-localization and references to it can be removed. + */} + + + + ); } From 0e8e5f1ea54d8ecd139b2d444a4d3149984570d4 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 7 Nov 2023 18:38:39 +0200 Subject: [PATCH 22/37] Update .env --- packages/create-sitecore-jss/src/templates/nextjs-xmcloud/.env | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/.env b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/.env index 4de981ed96..5292cf2bef 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/.env +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/.env @@ -1,3 +1,4 @@ + # ========== Sitecore Edge Platform =========== # Your unified Sitecore Edge Context Id. From 47a5c7dc23f19b6e5760cd1382c1d4065abf61cd Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 7 Nov 2023 18:49:46 +0200 Subject: [PATCH 23/37] Updated jsdoc --- .../src/templates/nextjs-xmcloud/src/Bootstrap.tsx | 6 +++++- .../src/templates/nextjs/src/Bootstrap.tsx | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/Bootstrap.tsx b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/Bootstrap.tsx index 0ab7a85687..b7154aa978 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/Bootstrap.tsx +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/Bootstrap.tsx @@ -3,11 +3,15 @@ import { initContext } from 'src/lib/context'; import { siteResolver } from 'lib/site-resolver'; import config from 'temp/config'; +/** + * The Bootstrap component is the entry point for performing any initialization logic + * that needs to happen early in the application's lifecycle. + */ const Bootstrap = (props: SitecorePageProps): JSX.Element | null => { const site = props.layoutData?.sitecore.context.site; const siteInfo = siteResolver.getByName(site?.name || config.siteName); - // Initialize the Context of the App + // Initialize the Context value for the app initContext({ siteName: siteInfo.name }); return null; diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/Bootstrap.tsx b/packages/create-sitecore-jss/src/templates/nextjs/src/Bootstrap.tsx index e596c140ed..a2679ac8da 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/Bootstrap.tsx +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/Bootstrap.tsx @@ -1,5 +1,9 @@ import { SitecorePageProps } from 'lib/page-props'; +/** + * The Bootstrap component is the entry point for performing any initialization logic + * that needs to happen early in the application's lifecycle. + */ const Bootstrap = (_props: SitecorePageProps): JSX.Element | null => { return null; }; From e7ec8f8639114233982d8be82b866a8171c3c718 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 7 Nov 2023 20:01:06 +0200 Subject: [PATCH 24/37] Avoid unused vars rule for Bootstrap --- .../create-sitecore-jss/src/templates/nextjs/src/Bootstrap.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/Bootstrap.tsx b/packages/create-sitecore-jss/src/templates/nextjs/src/Bootstrap.tsx index a2679ac8da..1bb368b21c 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/src/Bootstrap.tsx +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/Bootstrap.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ import { SitecorePageProps } from 'lib/page-props'; /** From 7769bfb8a7f9ea70fec2ee0d4b7993b1bbe5f7ab Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 7 Nov 2023 20:07:19 +0200 Subject: [PATCH 25/37] Normalize sitecoreEdgeUrl --- .../nextjs-xmcloud/scripts/config/plugins/edge-platform.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/scripts/config/plugins/edge-platform.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/scripts/config/plugins/edge-platform.ts index d8779ff0bb..9680df17ca 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/scripts/config/plugins/edge-platform.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/scripts/config/plugins/edge-platform.ts @@ -11,7 +11,9 @@ class EdgePlatformPlugin implements ConfigPlugin { order = 2; async exec(config: JssConfig) { - const sitecoreEdgeUrl = process.env[`${constantCase('sitecoreEdgeUrl')}`] || 'https://edge-platform.sitecorecloud.io'; + const sitecoreEdgeUrl = + process.env[`${constantCase('sitecoreEdgeUrl')}`]?.replace(/\/$/, '') || + 'https://edge-platform.sitecorecloud.io'; const sitecoreEdgeContextId = process.env[`${constantCase('sitecoreEdgeContextId')}`]; if (config.sitecoreApiKey && sitecoreEdgeContextId) { From 311c82056c0c6db5656ee86cdd812cbfdfadb4ca Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 7 Nov 2023 20:14:44 +0200 Subject: [PATCH 26/37] Updated CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf470c7e37..50d7aa8328 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,7 +43,8 @@ Our versioning strategy is as follows: * `[templates/nextjs]` `[sitecore-jss-nextjs]` CloudSDK Integration ([#1652](https://github.com/Sitecore/jss/pull/1652)): * Removed the following properties from _PersonalizeMiddleware_: _getPointOfSale_, _clientKey_, _endpoint_. You now need to provide _sitecoreEdgeContextId_ as a replacement. * _PersonalizeMiddleware_ has transitioned to utilizing the _CloudSDK_ package, replacing the previous dependency on _Engage_. - * Introduced _Context_ class, that is used to initialize the application Context and associated Software Development Kits (SDKs). Accessible within the _@sitecore-jss/sitecore-jss-nextjs/context_ submodule. + * Introduced _Context_ class, that is used to initialize the application Context and shared Software Development Kits (SDKs). Accessible within the _@sitecore-jss/sitecore-jss-nextjs/context_ submodule. + * Point of Sale resolution is fully removed, now it's handled by Sitecore Edge Proxy ## 21.5.0 From dc70eb774f9c9368c0acc3bb660a6babdac1e06c Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 7 Nov 2023 20:23:50 +0200 Subject: [PATCH 27/37] Updated BYOC initialization --- .../src/templates/nextjs-xmcloud/src/byoc/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/index.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/index.ts index 3e9486da3f..a47ea6d538 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/index.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/index.ts @@ -1,6 +1,6 @@ import * as FEAAS from '@sitecore-feaas/clientside/react'; import dynamic from 'next/dynamic'; -import { context } from 'lib/context'; +import { contextState } from 'lib/context'; /** * This is an out-of-box bundler for External components (BYOC) (see Sitecore documentation for more details) * It enables registering components in client-only or SSR/hybrid contexts @@ -8,7 +8,7 @@ import { context } from 'lib/context'; */ // Set context properties to be available within BYOC components -FEAAS.setContextProperties(context); +FEAAS.setContextProperties(contextState); // Import your client-only components via client-bundle. Nextjs's dynamic() call will ensure they are only rendered client-side const ClientBundle = dynamic(() => import('./index.client'), { From e1bb7856c86e64053e7286ff3209b1954279acbf Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 7 Nov 2023 20:32:12 +0200 Subject: [PATCH 28/37] Updated Props doc --- .../src/templates/nextjs-xmcloud/src/lib/context/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts index 946c93b209..65be0d6ea0 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts @@ -24,7 +24,7 @@ type SDKName = keyof typeof modules; type SDKs = { [name in SDKName]: typeof modules[name]['sdk'] }; /** - * Plugin's incoming properties + * Initial properties for the application Context initialization. */ export interface Props { siteName: string; From 2d8633b30048c1f1f0e639724bc332c1dc3e0888 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Wed, 8 Nov 2023 12:11:53 +0200 Subject: [PATCH 29/37] Updates --- .../src/templates/nextjs-xmcloud/package.json | 2 +- .../nextjs-xmcloud/src/byoc/index.ts | 4 +- .../nextjs-xmcloud/src/lib/context/index.ts | 16 ++--- .../src/lib/context/sdk/events.ts | 36 +++++------ packages/sitecore-jss-nextjs/package.json | 2 +- .../src/context/context.test.ts | 57 ++++-------------- .../src/context/context.ts | 59 +++++++------------ .../sitecore-jss-nextjs/src/context/index.ts | 2 +- packages/sitecore-jss-nextjs/src/index.ts | 2 +- .../src/middleware/personalize-middleware.ts | 13 ++-- 10 files changed, 68 insertions(+), 125 deletions(-) diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/package.json b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/package.json index 78b5fb83de..4394f65580 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/package.json +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/package.json @@ -1,6 +1,6 @@ { "dependencies": { - "@sitecore/components": "~1.0.19", + "@sitecore/components": "^1.1.0", "@sitecore-cloudsdk/events": "^0.1.1", "@sitecore-feaas/clientside": "^0.4.12" } diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/index.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/index.ts index a47ea6d538..3e9486da3f 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/index.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/index.ts @@ -1,6 +1,6 @@ import * as FEAAS from '@sitecore-feaas/clientside/react'; import dynamic from 'next/dynamic'; -import { contextState } from 'lib/context'; +import { context } from 'lib/context'; /** * This is an out-of-box bundler for External components (BYOC) (see Sitecore documentation for more details) * It enables registering components in client-only or SSR/hybrid contexts @@ -8,7 +8,7 @@ import { contextState } from 'lib/context'; */ // Set context properties to be available within BYOC components -FEAAS.setContextProperties(contextState); +FEAAS.setContextProperties(context); // Import your client-only components via client-bundle. Nextjs's dynamic() call will ensure they are only rendered client-side const ClientBundle = dynamic(() => import('./index.client'), { diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts index 65be0d6ea0..138aa95a7c 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts @@ -7,7 +7,7 @@ import Events from './sdk/events'; // Shape of the SDK object export type SDK = { sdk: SDKType; - init: () => Promise; + init: (contextInstance: typeof context) => Promise; }; /** @@ -24,7 +24,7 @@ type SDKName = keyof typeof modules; type SDKs = { [name in SDKName]: typeof modules[name]['sdk'] }; /** - * Initial properties for the application Context initialization. + * Properties that are passed to the Context. */ export interface Props { siteName: string; @@ -36,13 +36,9 @@ export interface Props { export const context = new Context({ sitecoreEdgeUrl: config.sitecoreEdgeUrl, sitecoreEdgeContextId: config.sitecoreEdgeContextId, + siteName: '', }); -/** - * The application Context State - */ -export let contextState = context.getState(); - /** * Initializes the application Context and associated Software Development Kits (SDKs). * This function is the entry point for setting up the application's context and any SDKs that are required for its proper functioning. @@ -55,14 +51,12 @@ export const initContext = async (props: Props) => { context.isInitialized = true; // Updating the context with the incoming properties - contextState = context.getState({ - siteName: props.siteName, - }); + context.siteName = props.siteName; // iterate over the SDKs and initialize them for (const sdkName of Object.keys(modules) as SDKName[]) { await context.initSDK(sdkName, async () => { - await modules[sdkName].init(); + await modules[sdkName].init(context); return modules[sdkName].sdk; }); diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/sdk/events.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/sdk/events.ts index 1cadafb04c..d33d556973 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/sdk/events.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/sdk/events.ts @@ -1,26 +1,22 @@ import * as Events from '@sitecore-cloudsdk/events/browser'; -import { contextState, SDK } from '../index'; +import { SDK } from '../index'; -const sdk = Events; +const sdkModule: SDK = { + sdk: Events, + init: async (context) => { + // Events module can't be initialized on the server side + // We also don't want to initialize it in development mode + if (typeof window === 'undefined' || process.env.NODE_ENV === 'development') return; -const init = async () => { - // Events module can't be initialized on the server side - // We also don't want to initialize it in development mode - if (typeof window === 'undefined' || process.env.NODE_ENV === 'development') return; - - return Events.init({ - siteName: contextState.siteName, - sitecoreEdgeContextId: contextState.sitecoreEdgeContextId, - // Replace with the top level cookie domain of the website that is being integrated e.g ".example.com" and not "www.example.com" - cookieDomain: window.location.hostname.replace(/^www\./, ''), - // Cookie may be created in personalize middleware (server), but if not we should create it here - enableBrowserCookie: true, - }); -}; - -const sdkModule: SDK = { - sdk, - init, + return Events.init({ + siteName: context.siteName, + sitecoreEdgeContextId: context.sitecoreEdgeContextId, + // Replace with the top level cookie domain of the website that is being integrated e.g ".example.com" and not "www.example.com" + cookieDomain: window.location.hostname.replace(/^www\./, ''), + // Cookie may be created in personalize middleware (server), but if not we should create it here + enableBrowserCookie: true, + }); + }, }; export default sdkModule; diff --git a/packages/sitecore-jss-nextjs/package.json b/packages/sitecore-jss-nextjs/package.json index 5cbcd479ad..bfc9cd8bf4 100644 --- a/packages/sitecore-jss-nextjs/package.json +++ b/packages/sitecore-jss-nextjs/package.json @@ -11,7 +11,7 @@ "test": "mocha --require ./test/setup.js \"./src/**/*.test.ts\" \"./src/**/*.test.tsx\" --exit", "prepublishOnly": "npm run build", "coverage": "nyc npm test", - "generate-docs": "npx typedoc --plugin typedoc-plugin-markdown --readme none --out ../../ref-docs/sitecore-jss-nextjs --entryPoints src/index.ts --entryPoints src/monitoring/index.ts --entryPoints src/editing/index.ts --entryPoints src/middleware/index.ts --entryPoints src/context/index.ts --githubPages false" + "generate-docs": "npx typedoc --plugin typedoc-plugin-markdown --readme none --out ../../ref-docs/sitecore-jss-nextjs --entryPoints src/index.ts --entryPoints src/monitoring/index.ts --entryPoints src/editing/index.ts --entryPoints src/middleware/index.ts --entryPoints src/context/index.ts --entryPoints src/utils/index.ts --entryPoints src/site/index.ts --entryPoints src/graphql/index.ts --githubPages false" }, "engines": { "node": ">=12", diff --git a/packages/sitecore-jss-nextjs/src/context/context.test.ts b/packages/sitecore-jss-nextjs/src/context/context.test.ts index 41c51db757..3010a7cf46 100644 --- a/packages/sitecore-jss-nextjs/src/context/context.test.ts +++ b/packages/sitecore-jss-nextjs/src/context/context.test.ts @@ -8,6 +8,7 @@ describe('Context', () => { const props = { sitecoreEdgeUrl: 'https://edgeurl', sitecoreEdgeContextId: 'contextid', + siteName: 'website', }; const sdks = { @@ -56,6 +57,16 @@ describe('Context', () => { sdks.Foo.init.reset(); }); + describe('constructor', () => { + it('should create a new context', () => { + const context = new Context(props); + + expect(context.sitecoreEdgeUrl).to.equal(props.sitecoreEdgeUrl); + expect(context.sitecoreEdgeContextId).to.equal(props.sitecoreEdgeContextId); + expect(context.siteName).to.equal(props.siteName); + }); + }); + describe('initSDK', () => { it('should initialize the SDKs', async () => { const context = new Context(props); @@ -93,53 +104,11 @@ describe('Context', () => { return; }), ]).then(() => { - expect(context['SDK'].Foo).to.deep.equal(sdks.Foo.sdk); - expect(context['SDK'].Bar).to.deep.equal(sdks.Bar.sdk); + expect(context.SDK.Foo).to.deep.equal(sdks.Foo.sdk); + expect(context.SDK.Bar).to.deep.equal(sdks.Bar.sdk); done(); }); }); }); - - describe('getState', () => { - it('should return the context state', () => { - const context = new Context(props); - - expect(context.getState({ siteName: 'xyz' })).to.deep.equal({ - sitecoreEdgeUrl: props.sitecoreEdgeUrl, - sitecoreEdgeContextId: props.sitecoreEdgeContextId, - SDK: {}, - siteName: 'xyz', - }); - }); - - it('should return the context state when siteName is not provided', () => { - const context = new Context(props); - - expect(context.getState()).to.deep.equal({ - sitecoreEdgeUrl: props.sitecoreEdgeUrl, - sitecoreEdgeContextId: props.sitecoreEdgeContextId, - SDK: {}, - siteName: '', - }); - }); - - it('should return the context state with SDKs', async () => { - const context = new Context(props); - - await context.initSDK('Foo', initFoo); - - await context.initSDK('Bar', initBar); - - expect(context.getState()).to.deep.equal({ - sitecoreEdgeUrl: props.sitecoreEdgeUrl, - sitecoreEdgeContextId: props.sitecoreEdgeContextId, - SDK: { - Foo: sdks.Foo.sdk, - Bar: sdks.Bar.sdk, - }, - siteName: '', - }); - }); - }); }); diff --git a/packages/sitecore-jss-nextjs/src/context/context.ts b/packages/sitecore-jss-nextjs/src/context/context.ts index 50eb55e8e9..919953ae27 100644 --- a/packages/sitecore-jss-nextjs/src/context/context.ts +++ b/packages/sitecore-jss-nextjs/src/context/context.ts @@ -1,7 +1,5 @@ -/** - * Context State that keeps track of the Context and Software Development Kits (SDKs) - */ -export interface ContextState { +// Configuration for the Context +export interface ContextConfig { /** * Your Sitecore Edge URL */ @@ -11,27 +9,11 @@ export interface ContextState { */ sitecoreEdgeContextId: string; /** - * Software Development Kits (SDKs) that are initialized by the ContextInitializer - */ - SDK: { [module in keyof SDKModules]?: SDKModules[module] }; - /** - * Site name + * Your Sitecore site name */ siteName: string; } -// Configuration for the Context -export interface ContextConfig { - /** - * Your Sitecore Edge URL - */ - sitecoreEdgeUrl: string; - /** - * Your Sitecore Edge Context ID - */ - sitecoreEdgeContextId: string; -} - /** * Context instance that is used to initialize the application Context and associated Software Development Kits (SDKs). */ @@ -40,33 +22,32 @@ export class Context { * Indicates whether the Context and SDK(s) have been initialized */ public isInitialized = false; - + /** + * The Sitecore Edge URL + */ + public readonly sitecoreEdgeUrl: string; + /** + * The Sitecore Edge Context ID + */ + public readonly sitecoreEdgeContextId: string; + /** + * The Sitecore site name + */ + public siteName: string; /** * Software Development Kits (SDKs) to be initialized */ - protected SDK: { [module in keyof SDKModules]?: SDKModules[module] } = {}; + public readonly SDK: { [module in keyof SDKModules]?: SDKModules[module] } = {}; /** * Promises for the SDKs */ protected promises: { [module in keyof SDKModules]?: Promise } = {}; - constructor(protected props: ContextConfig) {} - - /** - * Retrieves the context object. - * - * @param {Object} params incoming parameters - * @param {string} params.siteName Site name - * @returns context object - */ - public getState({ siteName = '' }: { siteName?: string } = {}): ContextState { - return { - sitecoreEdgeUrl: this.props.sitecoreEdgeUrl, - sitecoreEdgeContextId: this.props.sitecoreEdgeContextId, - SDK: this.SDK, - siteName, - }; + constructor(protected props: ContextConfig) { + this.sitecoreEdgeUrl = props.sitecoreEdgeUrl; + this.sitecoreEdgeContextId = props.sitecoreEdgeContextId; + this.siteName = props.siteName; } /** diff --git a/packages/sitecore-jss-nextjs/src/context/index.ts b/packages/sitecore-jss-nextjs/src/context/index.ts index a70da45fc7..7bcf16aa8d 100644 --- a/packages/sitecore-jss-nextjs/src/context/index.ts +++ b/packages/sitecore-jss-nextjs/src/context/index.ts @@ -1 +1 @@ -export { Context, ContextConfig, ContextState } from './context'; +export { Context, ContextConfig } from './context'; diff --git a/packages/sitecore-jss-nextjs/src/index.ts b/packages/sitecore-jss-nextjs/src/index.ts index b64dd719db..46da69054a 100644 --- a/packages/sitecore-jss-nextjs/src/index.ts +++ b/packages/sitecore-jss-nextjs/src/index.ts @@ -165,7 +165,7 @@ export { BYOCWrapper }; export { ComponentBuilder, ComponentBuilderConfig } from './ComponentBuilder'; -export { Context, ContextConfig, ContextState } from './context'; +export { Context, ContextConfig } from './context'; export { ComponentFactory, diff --git a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts index c115b74b19..000f439e32 100644 --- a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts +++ b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts @@ -10,6 +10,11 @@ import { MiddlewareBase, MiddlewareBaseConfig } from './middleware'; import { init, personalize } from '@sitecore-cloudsdk/personalize/server'; export type CdpServiceConfig = { + /** + * Your Sitecore Edge Platform endpoint + * Default is https://edge-platform.sitecorecloud.io + */ + sitecoreEdgeUrl?: string; /** * Your unified Sitecore Edge Context Id */ @@ -91,20 +96,19 @@ export class PersonalizeMiddleware extends MiddlewareBase { protected async initPersonalizeServer({ hostname, - sitecoreEdgeContextId, siteName, request, response, }: { hostname: string; - sitecoreEdgeContextId: string; siteName: string; request: NextRequest; response: NextResponse; }): Promise { await init( { - sitecoreEdgeContextId, + sitecoreEdgeUrl: this.config.cdpConfig.sitecoreEdgeUrl, + sitecoreEdgeContextId: this.config.cdpConfig.sitecoreEdgeContextId, siteName, cookieDomain: hostname, enableServerCookie: true, @@ -130,7 +134,7 @@ export class PersonalizeMiddleware extends MiddlewareBase { ) { const personalizationData = { channel: this.config.cdpConfig.channel || 'WEB', - currency: this.config.cdpConfig.currency ?? 'USA', + currency: this.config.cdpConfig.currency ?? 'USD', friendlyId: personalizeInfo.contentId, params, language, @@ -219,7 +223,6 @@ export class PersonalizeMiddleware extends MiddlewareBase { await this.initPersonalizeServer({ hostname, siteName: site.name, - sitecoreEdgeContextId: this.config.cdpConfig.sitecoreEdgeContextId, request: req, response, }); From 924cb4088850d7cfa940778633c7ef96f01bc5f6 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Wed, 8 Nov 2023 12:15:54 +0200 Subject: [PATCH 30/37] Updated unit test --- .../middleware/personalize-middleware.test.ts | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.test.ts b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.test.ts index 7618545b66..7383065f00 100644 --- a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.test.ts +++ b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.test.ts @@ -7,7 +7,6 @@ import sinon, { spy } from 'sinon'; import nextjs, { NextRequest, NextResponse } from 'next/server'; import { debug } from '@sitecore-jss/sitecore-jss'; import { SiteResolver } from '@sitecore-jss/sitecore-jss/site'; -import { ExperienceParams } from '@sitecore-jss/sitecore-jss/personalize'; import { PersonalizeMiddleware } from './personalize-middleware'; use(sinonChai); @@ -33,17 +32,7 @@ describe('PersonalizeMiddleware', () => { const variantIds = ['variant-1', 'variant-2']; const contentId = `${id}_en_${version}`.toLowerCase(); const defaultLang = 'en'; - const pointOfSale = 'cdp-pos'; const referrer = 'http://localhost:3000'; - const experienceParams: ExperienceParams = { - referrer, - utm: { - campaign: 'utm_campaign', - content: null, - medium: null, - source: null, - }, - }; const createRequest = (props: any = {}) => { const req = { ...props, @@ -164,8 +153,8 @@ describe('PersonalizeMiddleware', () => { } = {} ) => { const cdpConfig = { - clientKey: 'cdp-client-key', - endpoint: 'http://cdp-endpoint', + sitecoreEdgeContextId: '0000-0000-0000', + sitecoreEdgeUrl: 'https://foo.bar', ...(props?.cdpConfig || {}), }; const edgeConfig = { @@ -179,18 +168,12 @@ describe('PersonalizeMiddleware', () => { name: siteName, language: props.language || '', hostName: hostname, - pointOfSale: { - [props.language || defaultLang]: pointOfSale, - }, })); getByHost = sinon.stub().callsFake((hostName: string) => ({ name: siteName, language: props.language || '', hostName, - pointOfSale: { - [props.language || defaultLang]: pointOfSale, - }, })); } From 2ee000b2a8b51ef00fedc8fa3224cb89936441c6 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Wed, 8 Nov 2023 12:18:17 +0200 Subject: [PATCH 31/37] Updated comment --- .../src/templates/nextjs-xmcloud/src/lib/context/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts index 138aa95a7c..bf6be79978 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts @@ -45,7 +45,7 @@ export const context = new Context({ * It prepares the resources needed to interact with various services and features within the application. */ export const initContext = async (props: Props) => { - // Context and SDKs are initialized only once + // Context can be initialized only once if (context.isInitialized) return; context.isInitialized = true; From 4315eab352dd9c2705e99ef0a40c8aef830aff49 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Wed, 8 Nov 2023 16:03:58 +0200 Subject: [PATCH 32/37] Provided sitecoreEdgeUrl to the middleware --- .../nextjs-xmcloud/src/lib/middleware/plugins/personalize.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/middleware/plugins/personalize.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/middleware/plugins/personalize.ts index 9f969bd9e0..6580931843 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/middleware/plugins/personalize.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/middleware/plugins/personalize.ts @@ -34,6 +34,7 @@ class PersonalizePlugin implements MiddlewarePlugin { }, // Configuration for your Sitecore CDP endpoint cdpConfig: { + sitecoreEdgeUrl: config.sitecoreEdgeUrl, sitecoreEdgeContextId: config.sitecoreEdgeContextId, timeout: (process.env.PERSONALIZE_MIDDLEWARE_CDP_TIMEOUT && From 5e609445804791fe5fd6df6e4ffbc0d3575feb15 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Wed, 8 Nov 2023 21:11:36 +0200 Subject: [PATCH 33/37] Refactoring --- .../nextjs-xmcloud/src/Bootstrap.tsx | 10 +- .../nextjs-xmcloud/src/lib/context/index.ts | 51 +------ .../src/lib/context/sdk/events.ts | 7 +- .../src/context/context.test.ts | 143 ++++++++++++------ .../src/context/context.ts | 95 +++++++++--- .../sitecore-jss-nextjs/src/context/index.ts | 2 +- packages/sitecore-jss-nextjs/src/index.ts | 2 +- 7 files changed, 183 insertions(+), 127 deletions(-) diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/Bootstrap.tsx b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/Bootstrap.tsx index b7154aa978..a8f082c8bb 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/Bootstrap.tsx +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/Bootstrap.tsx @@ -1,5 +1,5 @@ import { SitecorePageProps } from 'lib/page-props'; -import { initContext } from 'src/lib/context'; +import { context } from 'src/lib/context'; import { siteResolver } from 'lib/site-resolver'; import config from 'temp/config'; @@ -11,8 +11,12 @@ const Bootstrap = (props: SitecorePageProps): JSX.Element | null => { const site = props.layoutData?.sitecore.context.site; const siteInfo = siteResolver.getByName(site?.name || config.siteName); - // Initialize the Context value for the app - initContext({ siteName: siteInfo.name }); + /** + * Initializes the application Context and associated Software Development Kits (SDKs). + * This function is the entry point for setting up the application's context and any SDKs that are required for its proper functioning. + * It prepares the resources needed to interact with various services and features within the application. + */ + context.init({ siteName: siteInfo.name }); return null; }; diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts index bf6be79978..6a55d862fb 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts @@ -1,67 +1,22 @@ -import * as FEAAS from '@sitecore-feaas/clientside/react'; import { Context } from '@sitecore-jss/sitecore-jss-nextjs/context'; import config from 'temp/config'; import Events from './sdk/events'; -// Shape of the SDK object -export type SDK = { - sdk: SDKType; - init: (contextInstance: typeof context) => Promise; -}; - /** * List of SDKs to be initialized. * Each SDK is defined as a module with the @type {SDK} type. */ -const modules = { +const sdks = { Events, }; -type SDKName = keyof typeof modules; - -// SDKs that are initialized by the Context -type SDKs = { [name in SDKName]: typeof modules[name]['sdk'] }; - -/** - * Properties that are passed to the Context. - */ -export interface Props { - siteName: string; -} - /** * Context instance that is used to initialize the application Context and associated Software Development Kits (SDKs). */ -export const context = new Context({ +export const context = new Context({ sitecoreEdgeUrl: config.sitecoreEdgeUrl, sitecoreEdgeContextId: config.sitecoreEdgeContextId, siteName: '', + SDK: sdks, }); - -/** - * Initializes the application Context and associated Software Development Kits (SDKs). - * This function is the entry point for setting up the application's context and any SDKs that are required for its proper functioning. - * It prepares the resources needed to interact with various services and features within the application. - */ -export const initContext = async (props: Props) => { - // Context can be initialized only once - if (context.isInitialized) return; - - context.isInitialized = true; - - // Updating the context with the incoming properties - context.siteName = props.siteName; - - // iterate over the SDKs and initialize them - for (const sdkName of Object.keys(modules) as SDKName[]) { - await context.initSDK(sdkName, async () => { - await modules[sdkName].init(context); - - return modules[sdkName].sdk; - }); - } - - // Setting the context properties for the FEAAS SDK - FEAAS.setContextProperties(context); -}; diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/sdk/events.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/sdk/events.ts index d33d556973..5ac89493c9 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/sdk/events.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/sdk/events.ts @@ -1,14 +1,15 @@ import * as Events from '@sitecore-cloudsdk/events/browser'; -import { SDK } from '../index'; +import { SDK } from '@sitecore-jss/sitecore-jss-nextjs/context'; +import { context } from '../index'; const sdkModule: SDK = { sdk: Events, - init: async (context) => { + init: async () => { // Events module can't be initialized on the server side // We also don't want to initialize it in development mode if (typeof window === 'undefined' || process.env.NODE_ENV === 'development') return; - return Events.init({ + await Events.init({ siteName: context.siteName, sitecoreEdgeContextId: context.sitecoreEdgeContextId, // Replace with the top level cookie domain of the website that is being integrated e.g ".example.com" and not "www.example.com" diff --git a/packages/sitecore-jss-nextjs/src/context/context.test.ts b/packages/sitecore-jss-nextjs/src/context/context.test.ts index 3010a7cf46..769cf863cf 100644 --- a/packages/sitecore-jss-nextjs/src/context/context.test.ts +++ b/packages/sitecore-jss-nextjs/src/context/context.test.ts @@ -5,61 +5,46 @@ import { expect } from 'chai'; import { Context } from './'; describe('Context', () => { - const props = { - sitecoreEdgeUrl: 'https://edgeurl', - sitecoreEdgeContextId: 'contextid', - siteName: 'website', - }; - const sdks = { Foo: { sdk: { foo: true }, - init: sinon.stub().callsFake(() => { - return new Promise((resolve) => { + init: () => + new Promise((resolve) => { setTimeout(() => { - resolve(sdks.Foo.sdk); + resolve(); }, 300); - }); - }), + }), }, Bar: { sdk: { bar: true }, - create: sinon.stub().callsFake(() => { - return new Promise((resolve) => { + init: () => { + return new Promise((resolve) => { setTimeout(() => { - resolve(sdks.Bar.sdk); + resolve(); }, 500); }); - }), + }, }, }; - const initFoo = async () => { - await sdks.Foo.init(); - - return sdks.Foo.sdk; - }; - - const initBar = async () => { - await sdks.Bar.create(); - - return sdks.Bar.sdk; - }; - - type SDKName = keyof typeof sdks; + const fooInitSpy = sinon.spy(sdks.Foo, 'init'); + const barInitSpy = sinon.spy(sdks.Bar, 'init'); - type SDKs = { - [name in SDKName]: typeof sdks[name]['sdk']; + const props = { + sitecoreEdgeUrl: 'https://edgeurl', + sitecoreEdgeContextId: 'contextid', + siteName: '', + SDK: sdks, }; afterEach(() => { - sdks.Bar.create.reset(); - sdks.Foo.init.reset(); + fooInitSpy.resetHistory(); + barInitSpy.resetHistory(); }); describe('constructor', () => { it('should create a new context', () => { - const context = new Context(props); + const context = new Context(props); expect(context.sitecoreEdgeUrl).to.equal(props.sitecoreEdgeUrl); expect(context.sitecoreEdgeContextId).to.equal(props.sitecoreEdgeContextId); @@ -67,38 +52,102 @@ describe('Context', () => { }); }); - describe('initSDK', () => { - it('should initialize the SDKs', async () => { - const context = new Context(props); + describe('init', () => { + it('should initialize the context', (done) => { + const context = new Context(props); + + context.init(); + + expect(context.isInitialized).to.be.true; + expect(context.siteName).to.equal(props.siteName); + + expect(context.SDK.Bar).to.equal(undefined); + expect(context.SDK.Foo).to.equal(undefined); + + Promise.all([ + context.getSDK('Foo')?.then((sdk) => { + expect(fooInitSpy.calledOnce).to.be.true; + expect(sdk).to.deep.equal(sdks.Foo.sdk); + + return; + }), + context.getSDK('Bar')?.then((sdk) => { + expect(barInitSpy.calledOnce).to.be.true; + expect(sdk).to.deep.equal(sdks.Bar.sdk); + + return; + }), + ]).then(() => { + expect(context.SDK.Foo).to.deep.equal(sdks.Foo.sdk); + expect(context.SDK.Bar).to.deep.equal(sdks.Bar.sdk); + + done(); + }); + }); + + it('should initialize the context with a different site name', (done) => { + const context = new Context(props); + + context.init({ siteName: 'website' }); + + expect(context.isInitialized).to.be.true; + expect(context.siteName).to.equal('website'); + + expect(context.SDK.Bar).to.equal(undefined); + expect(context.SDK.Foo).to.equal(undefined); + + Promise.all([ + context.getSDK('Foo')?.then((sdk) => { + expect(fooInitSpy.calledOnce).to.be.true; + expect(sdk).to.deep.equal(sdks.Foo.sdk); + + return; + }), + context.getSDK('Bar')?.then((sdk) => { + expect(barInitSpy.calledOnce).to.be.true; + expect(sdk).to.deep.equal(sdks.Bar.sdk); + + return; + }), + ]).then(() => { + expect(context.SDK.Foo).to.deep.equal(sdks.Foo.sdk); + expect(context.SDK.Bar).to.deep.equal(sdks.Bar.sdk); + + done(); + }); + }); - await context.initSDK('Foo', initFoo); + it('should not initialize the context if it is already initialized', () => { + const context = new Context(props); - await context.initSDK('Bar', initBar); + context.init({ siteName: 'website-1' }); - expect(context['SDK'].Foo).to.deep.equal(sdks.Foo.sdk); - expect(context['SDK'].Bar).to.deep.equal(sdks.Bar.sdk); + expect(context.isInitialized).to.be.true; - expect(sdks.Foo.init.calledOnce).to.be.true; - expect(sdks.Bar.create.calledOnce).to.be.true; + context.init({ siteName: 'website-2' }); + + expect(context.siteName).to.equal('website-1'); }); }); describe('getSDK', () => { it('should return the SDKs', (done) => { - const context = new Context(props); + const context = new Context(props); + + context.init(); - context.initSDK('Foo', initFoo); - context.initSDK('Bar', initBar); + expect(context.SDK.Bar).to.equal(undefined); + expect(context.SDK.Foo).to.equal(undefined); Promise.all([ context.getSDK('Foo')?.then((sdk) => { - expect(sdks.Foo.init.calledOnce).to.be.true; + expect(fooInitSpy.calledOnce).to.be.true; expect(sdk).to.deep.equal(sdks.Foo.sdk); return; }), context.getSDK('Bar')?.then((sdk) => { - expect(sdks.Bar.create.calledOnce).to.be.true; + expect(barInitSpy.calledOnce).to.be.true; expect(sdk).to.deep.equal(sdks.Bar.sdk); return; diff --git a/packages/sitecore-jss-nextjs/src/context/context.ts b/packages/sitecore-jss-nextjs/src/context/context.ts index 919953ae27..cc0ea8a6cd 100644 --- a/packages/sitecore-jss-nextjs/src/context/context.ts +++ b/packages/sitecore-jss-nextjs/src/context/context.ts @@ -1,5 +1,36 @@ -// Configuration for the Context -export interface ContextConfig { +/** + * Software Development Kit (SDK) instance + */ +export type SDK = { + /** + * The Software Development Kit (SDK) library instance + */ + sdk: SDKType; + /** + * Initializes the Software Development Kit (SDK) + */ + init: () => Promise; +}; + +/** + * Software Development Kits (SDKs) to be initialized + */ +type SDKModulesType = Record; + +/** + * Properties that are passed to the Context. + */ +export interface ContextInitProps { + /** + * Your Sitecore site name + */ + siteName?: string; +} + +/** + * Configuration that is passed to the Context. + */ +export interface ContextConfig { /** * Your Sitecore Edge URL */ @@ -12,12 +43,16 @@ export interface ContextConfig { * Your Sitecore site name */ siteName: string; + /** + * Software Development Kits (SDKs) to be initialized + */ + SDK: { [module in keyof SDKModules]: SDKModules[module] }; } /** * Context instance that is used to initialize the application Context and associated Software Development Kits (SDKs). */ -export class Context { +export class Context { /** * Indicates whether the Context and SDK(s) have been initialized */ @@ -37,36 +72,32 @@ export class Context { /** * Software Development Kits (SDKs) to be initialized */ - public readonly SDK: { [module in keyof SDKModules]?: SDKModules[module] } = {}; - + public readonly SDK: { [module in keyof SDKModules]?: SDKModules[module]['sdk'] } = {}; /** * Promises for the SDKs */ - protected promises: { [module in keyof SDKModules]?: Promise } = {}; + protected sdkPromises: { [module in keyof SDKModules]?: Promise } = {}; - constructor(protected props: ContextConfig) { + constructor(protected props: ContextConfig) { this.sitecoreEdgeUrl = props.sitecoreEdgeUrl; this.sitecoreEdgeContextId = props.sitecoreEdgeContextId; this.siteName = props.siteName; } - /** - * Initializes the Software Development Kit (SDK). - * This function is the entry point for setting up the SDK. - * - * @param {string} name SDK name - * @param {Function} cb Callback function that initializes the SDK - */ - public async initSDK(name: T, cb: () => Promise) { - this.promises[name] = new Promise((resolve) => { - cb().then((sdk) => { - this.SDK[name] = sdk; + public init(props: ContextInitProps = {}) { + // Context and SDKs are initialized only once + if (this.isInitialized) return; - resolve(sdk); - }); - }); + this.isInitialized = true; - await this.promises[name]; + if (props.siteName) { + this.siteName = props.siteName; + } + + // iterate over the SDKs and initialize them + for (const sdkName of Object.keys(this.props.SDK) as (keyof SDKModules)[]) { + this.initSDK(sdkName); + } } /** @@ -75,7 +106,23 @@ export class Context { * @param {string} name SDK name * @returns initialized SDK */ - public getSDK(name: T): Promise | undefined { - return this.promises[name]; + public getSDK(name: T): Promise | undefined { + return this.sdkPromises[name]; + } + + /** + * Initializes the Software Development Kit (SDK) + * + * @param {T} name SDK name + * @returns {void} + */ + protected initSDK(name: T): void { + this.sdkPromises[name] = new Promise((resolve) => { + this.props.SDK[name].init().then(() => { + this.SDK[name] = this.props.SDK[name].sdk; + + resolve(this.SDK[name]); + }); + }); } } diff --git a/packages/sitecore-jss-nextjs/src/context/index.ts b/packages/sitecore-jss-nextjs/src/context/index.ts index 7bcf16aa8d..b7719c3c1b 100644 --- a/packages/sitecore-jss-nextjs/src/context/index.ts +++ b/packages/sitecore-jss-nextjs/src/context/index.ts @@ -1 +1 @@ -export { Context, ContextConfig } from './context'; +export { Context, ContextConfig, SDK } from './context'; diff --git a/packages/sitecore-jss-nextjs/src/index.ts b/packages/sitecore-jss-nextjs/src/index.ts index 46da69054a..7335fa4782 100644 --- a/packages/sitecore-jss-nextjs/src/index.ts +++ b/packages/sitecore-jss-nextjs/src/index.ts @@ -165,7 +165,7 @@ export { BYOCWrapper }; export { ComponentBuilder, ComponentBuilderConfig } from './ComponentBuilder'; -export { Context, ContextConfig } from './context'; +export { Context, ContextConfig, SDK } from './context'; export { ComponentFactory, From d08ba22b4d18f368f0c578204caa7f1c71295c2c Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Wed, 8 Nov 2023 21:33:23 +0200 Subject: [PATCH 34/37] Passing props to SDK's --- .../nextjs-xmcloud/src/lib/context/index.ts | 2 +- .../nextjs-xmcloud/src/lib/context/sdk/events.ts | 7 +++---- packages/sitecore-jss-nextjs/src/context/context.ts | 12 ++++++++++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts index 6a55d862fb..ad5a13fa4d 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts @@ -17,6 +17,6 @@ const sdks = { export const context = new Context({ sitecoreEdgeUrl: config.sitecoreEdgeUrl, sitecoreEdgeContextId: config.sitecoreEdgeContextId, - siteName: '', + siteName: config.siteName, SDK: sdks, }); diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/sdk/events.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/sdk/events.ts index 5ac89493c9..59d8e7629b 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/sdk/events.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/sdk/events.ts @@ -1,17 +1,16 @@ import * as Events from '@sitecore-cloudsdk/events/browser'; import { SDK } from '@sitecore-jss/sitecore-jss-nextjs/context'; -import { context } from '../index'; const sdkModule: SDK = { sdk: Events, - init: async () => { + init: async (props) => { // Events module can't be initialized on the server side // We also don't want to initialize it in development mode if (typeof window === 'undefined' || process.env.NODE_ENV === 'development') return; await Events.init({ - siteName: context.siteName, - sitecoreEdgeContextId: context.sitecoreEdgeContextId, + siteName: props.siteName, + sitecoreEdgeContextId: props.sitecoreEdgeContextId, // Replace with the top level cookie domain of the website that is being integrated e.g ".example.com" and not "www.example.com" cookieDomain: window.location.hostname.replace(/^www\./, ''), // Cookie may be created in personalize middleware (server), but if not we should create it here diff --git a/packages/sitecore-jss-nextjs/src/context/context.ts b/packages/sitecore-jss-nextjs/src/context/context.ts index cc0ea8a6cd..1fa0898fa3 100644 --- a/packages/sitecore-jss-nextjs/src/context/context.ts +++ b/packages/sitecore-jss-nextjs/src/context/context.ts @@ -9,7 +9,7 @@ export type SDK = { /** * Initializes the Software Development Kit (SDK) */ - init: () => Promise; + init: (props: InitSDKProps) => Promise; }; /** @@ -49,6 +49,14 @@ export interface ContextConfig { SDK: { [module in keyof SDKModules]: SDKModules[module] }; } +/** + * Properties that are passed to the Software Development Kit (SDK) initialization function. + */ +type InitSDKProps = Pick< + ContextConfig, + 'siteName' | 'sitecoreEdgeContextId' | 'sitecoreEdgeUrl' +>; + /** * Context instance that is used to initialize the application Context and associated Software Development Kits (SDKs). */ @@ -118,7 +126,7 @@ export class Context { */ protected initSDK(name: T): void { this.sdkPromises[name] = new Promise((resolve) => { - this.props.SDK[name].init().then(() => { + this.props.SDK[name].init(this).then(() => { this.SDK[name] = this.props.SDK[name].sdk; resolve(this.SDK[name]); From 3712ea5d4159035eee93594d24bf6b9a9610b3f7 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Wed, 8 Nov 2023 21:40:16 +0200 Subject: [PATCH 35/37] Updated type --- packages/sitecore-jss-nextjs/src/context/context.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/sitecore-jss-nextjs/src/context/context.ts b/packages/sitecore-jss-nextjs/src/context/context.ts index 1fa0898fa3..5be0492985 100644 --- a/packages/sitecore-jss-nextjs/src/context/context.ts +++ b/packages/sitecore-jss-nextjs/src/context/context.ts @@ -52,10 +52,7 @@ export interface ContextConfig { /** * Properties that are passed to the Software Development Kit (SDK) initialization function. */ -type InitSDKProps = Pick< - ContextConfig, - 'siteName' | 'sitecoreEdgeContextId' | 'sitecoreEdgeUrl' ->; +type InitSDKProps = Omit, 'SDK'>; /** * Context instance that is used to initialize the application Context and associated Software Development Kits (SDKs). From defd3f319e465ce0c0aea4286b46bba371ab564e Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Wed, 8 Nov 2023 22:05:13 +0200 Subject: [PATCH 36/37] Renamed SDKs -> sdks --- .../nextjs-xmcloud/src/lib/context/index.ts | 2 +- .../src/context/context.test.ts | 26 +++++++++---------- .../src/context/context.ts | 14 +++++----- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts index ad5a13fa4d..8ba26ce19f 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts @@ -18,5 +18,5 @@ export const context = new Context({ sitecoreEdgeUrl: config.sitecoreEdgeUrl, sitecoreEdgeContextId: config.sitecoreEdgeContextId, siteName: config.siteName, - SDK: sdks, + sdks, }); diff --git a/packages/sitecore-jss-nextjs/src/context/context.test.ts b/packages/sitecore-jss-nextjs/src/context/context.test.ts index 769cf863cf..8d7f528500 100644 --- a/packages/sitecore-jss-nextjs/src/context/context.test.ts +++ b/packages/sitecore-jss-nextjs/src/context/context.test.ts @@ -34,7 +34,7 @@ describe('Context', () => { sitecoreEdgeUrl: 'https://edgeurl', sitecoreEdgeContextId: 'contextid', siteName: '', - SDK: sdks, + sdks, }; afterEach(() => { @@ -61,8 +61,8 @@ describe('Context', () => { expect(context.isInitialized).to.be.true; expect(context.siteName).to.equal(props.siteName); - expect(context.SDK.Bar).to.equal(undefined); - expect(context.SDK.Foo).to.equal(undefined); + expect(context.sdks.Bar).to.equal(undefined); + expect(context.sdks.Foo).to.equal(undefined); Promise.all([ context.getSDK('Foo')?.then((sdk) => { @@ -78,8 +78,8 @@ describe('Context', () => { return; }), ]).then(() => { - expect(context.SDK.Foo).to.deep.equal(sdks.Foo.sdk); - expect(context.SDK.Bar).to.deep.equal(sdks.Bar.sdk); + expect(context.sdks.Foo).to.deep.equal(sdks.Foo.sdk); + expect(context.sdks.Bar).to.deep.equal(sdks.Bar.sdk); done(); }); @@ -93,8 +93,8 @@ describe('Context', () => { expect(context.isInitialized).to.be.true; expect(context.siteName).to.equal('website'); - expect(context.SDK.Bar).to.equal(undefined); - expect(context.SDK.Foo).to.equal(undefined); + expect(context.sdks.Bar).to.equal(undefined); + expect(context.sdks.Foo).to.equal(undefined); Promise.all([ context.getSDK('Foo')?.then((sdk) => { @@ -110,8 +110,8 @@ describe('Context', () => { return; }), ]).then(() => { - expect(context.SDK.Foo).to.deep.equal(sdks.Foo.sdk); - expect(context.SDK.Bar).to.deep.equal(sdks.Bar.sdk); + expect(context.sdks.Foo).to.deep.equal(sdks.Foo.sdk); + expect(context.sdks.Bar).to.deep.equal(sdks.Bar.sdk); done(); }); @@ -136,8 +136,8 @@ describe('Context', () => { context.init(); - expect(context.SDK.Bar).to.equal(undefined); - expect(context.SDK.Foo).to.equal(undefined); + expect(context.sdks.Bar).to.equal(undefined); + expect(context.sdks.Foo).to.equal(undefined); Promise.all([ context.getSDK('Foo')?.then((sdk) => { @@ -153,8 +153,8 @@ describe('Context', () => { return; }), ]).then(() => { - expect(context.SDK.Foo).to.deep.equal(sdks.Foo.sdk); - expect(context.SDK.Bar).to.deep.equal(sdks.Bar.sdk); + expect(context.sdks.Foo).to.deep.equal(sdks.Foo.sdk); + expect(context.sdks.Bar).to.deep.equal(sdks.Bar.sdk); done(); }); diff --git a/packages/sitecore-jss-nextjs/src/context/context.ts b/packages/sitecore-jss-nextjs/src/context/context.ts index 5be0492985..89fc384544 100644 --- a/packages/sitecore-jss-nextjs/src/context/context.ts +++ b/packages/sitecore-jss-nextjs/src/context/context.ts @@ -46,13 +46,13 @@ export interface ContextConfig { /** * Software Development Kits (SDKs) to be initialized */ - SDK: { [module in keyof SDKModules]: SDKModules[module] }; + sdks: { [module in keyof SDKModules]: SDKModules[module] }; } /** * Properties that are passed to the Software Development Kit (SDK) initialization function. */ -type InitSDKProps = Omit, 'SDK'>; +type InitSDKProps = Omit, 'sdks'>; /** * Context instance that is used to initialize the application Context and associated Software Development Kits (SDKs). @@ -77,7 +77,7 @@ export class Context { /** * Software Development Kits (SDKs) to be initialized */ - public readonly SDK: { [module in keyof SDKModules]?: SDKModules[module]['sdk'] } = {}; + public readonly sdks: { [module in keyof SDKModules]?: SDKModules[module]['sdk'] } = {}; /** * Promises for the SDKs */ @@ -100,7 +100,7 @@ export class Context { } // iterate over the SDKs and initialize them - for (const sdkName of Object.keys(this.props.SDK) as (keyof SDKModules)[]) { + for (const sdkName of Object.keys(this.props.sdks) as (keyof SDKModules)[]) { this.initSDK(sdkName); } } @@ -123,10 +123,10 @@ export class Context { */ protected initSDK(name: T): void { this.sdkPromises[name] = new Promise((resolve) => { - this.props.SDK[name].init(this).then(() => { - this.SDK[name] = this.props.SDK[name].sdk; + this.props.sdks[name].init(this).then(() => { + this.sdks[name] = this.props.sdks[name].sdk; - resolve(this.SDK[name]); + resolve(this.sdks[name]); }); }); } From 72c3c2338fc276a1680507a46cabfd89af008c64 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Wed, 8 Nov 2023 22:09:10 +0200 Subject: [PATCH 37/37] Updated yarn.lock --- yarn.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 534e8ea514..bcc41b6678 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6650,7 +6650,6 @@ __metadata: "@sitecore-jss/sitecore-jss": 21.7.0-canary.6 "@sitecore-jss/sitecore-jss-dev-tools": 21.7.0-canary.6 "@sitecore-jss/sitecore-jss-react": 21.7.0-canary.6 - "@sitecore/engage": ^1.4.1 "@types/chai": ^4.3.4 "@types/chai-as-promised": ^7.1.5 "@types/chai-string": ^1.4.2