From 16daf81786fb9ad4d1e8b4b5d081d4c4724c1f84 Mon Sep 17 00:00:00 2001 From: Artem Alexeyenko Date: Thu, 31 Oct 2024 14:16:46 -0400 Subject: [PATCH] [nextjs] Update Cloudsdk to v0.4 (#1933) --- CHANGELOG.md | 9 + docs/upgrades/22.x/22.2.md | 129 +++++++++ .../src/templates/nextjs-xmcloud/package.json | 5 +- .../nextjs-xmcloud/src/Bootstrap.tsx | 35 ++- .../src/byoc/{index.ts => index.tsx} | 33 ++- .../src/components/CdpPageView.tsx | 23 +- .../nextjs-xmcloud/src/lib/context/index.ts | 22 -- .../src/lib/context/sdk/events.ts | 26 -- packages/sitecore-jss-nextjs/context.d.ts | 1 - packages/sitecore-jss-nextjs/context.js | 1 - packages/sitecore-jss-nextjs/package.json | 7 +- .../src/context/context.test.ts | 248 ------------------ .../src/context/context.ts | 164 ------------ .../sitecore-jss-nextjs/src/context/index.ts | 1 - packages/sitecore-jss-nextjs/src/index.ts | 2 - .../src/middleware/personalize-middleware.ts | 9 +- yarn.lock | 48 ++-- 17 files changed, 240 insertions(+), 523 deletions(-) create mode 100644 docs/upgrades/22.x/22.2.md rename packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/{index.ts => index.tsx} (57%) delete mode 100644 packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts delete mode 100644 packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/sdk/events.ts delete mode 100644 packages/sitecore-jss-nextjs/context.d.ts delete mode 100644 packages/sitecore-jss-nextjs/context.js delete mode 100644 packages/sitecore-jss-nextjs/src/context/context.test.ts delete mode 100644 packages/sitecore-jss-nextjs/src/context/context.ts delete mode 100644 packages/sitecore-jss-nextjs/src/context/index.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 840bc9c63d..cad9b54c6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,15 @@ Our versioning strategy is as follows: - Minor: may include breaking changes in framework packages (e.g. framework upgrades, new features, improvements) - Major: may include breaking changes in core packages (e.g. major architectural changes, major features) +## 22.2.0 + +### 🛠 Breaking Change + +* `[templates/nextjs-xmcloud]` CloudSDK dependencies have been updated to 0.4.0 ([#1933](https://github.com/Sitecore/jss/pull/1933)) +* `[templates/nextjs-xmcloud]` `@sitecore/components` dependency has been updated to 2.0.0 ([#1933](https://github.com/Sitecore/jss/pull/1933)) +* `[templates/nextjs-xmcloud]` `lib/context` import has been removed. Values from `temp/config` can be used instead. ([#1933](https://github.com/Sitecore/jss/pull/1933)) +* `[sitecore-jss-nextjs]` `Context` import and `@sitecore-jss/sitecore-jss-nextjs/context` submodule have been removed. ([#1933](https://github.com/Sitecore/jss/pull/1933)) + ## 22.1.4 ### 🐛 Bug Fixes diff --git a/docs/upgrades/22.x/22.2.md b/docs/upgrades/22.x/22.2.md new file mode 100644 index 0000000000..c135874e7c --- /dev/null +++ b/docs/upgrades/22.x/22.2.md @@ -0,0 +1,129 @@ +# Nextjs - XMCloud + +* Update the `@sitecore/components` dependency to `~2.0.0` +* Update the `@sitecore-cloudsdk/events` dependency to `^0.4.0` +* Add the dependency on `@sitecore-cloudsdk/core` with version `^0.4.0`. You should now have the below dependencies present: +``` + "@sitecore/components": "~2.0.0", + "@sitecore-cloudsdk/core": "^0.4.0", + "@sitecore-cloudsdk/events": "^0.4.0", +``` +* Remove the `src/lib/context` folder + +* Update `src/Bootstrap.tsx`: + * Remove the context import: + ``` + import { context } from 'src/lib/context'; + ``` + * Add imports required for CloudSDK setup: + ``` + import { CloudSDK } from '@sitecore-cloudsdk/core/browser'; + import '@sitecore-cloudsdk/events/browser'; + import { LayoutServicePageState } from '@sitecore-jss/sitecore-jss-nextjs'; + ``` + * Remove the context.init() call: + ``` + context.init({ + siteName: props.site?.name || config.sitecoreSiteName, + pageState: props.layoutData?.sitecore?.context?.pageState, + }); + ``` + * Replace it with CloudSDK initialization, making sure it is performed within `useEffect()` and only in normal, non-dev mode: + ``` + useEffect(() => { + const pageState = props.layoutData?.sitecore?.context.pageState; + if (process.env.NODE_ENV === 'development') + console.debug('Browser Events SDK is not initialized in development environment'); + else if (pageState !== LayoutServicePageState.Normal) + console.debug('Browser Events SDK is not initialized in edit and preview modes'); + else { + CloudSDK({ + sitecoreEdgeUrl: config.sitecoreEdgeUrl, + sitecoreEdgeContextId: config.sitecoreEdgeContextId, + siteName: props.site?.name || config.sitecoreSiteName, + enableBrowserCookie: true, + // 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\./, ''), + }) + .addEvents() + .initialize(); + } + }, [props.site?.name]); + ``` + +* Update `src/components/CDPPageView.tsx`: + * Remove the context import: + ``` + import { context } from 'lib/context'; + ``` + * Add import for CloudSDK: + ``` + import { pageView } from '@sitecore-cloudsdk/events/browser'; + ``` + * Replace the context promise code + ``` + context + .getSDK('Events') + .then((Events) => + Events.pageView({ + channel: 'WEB', + currency: 'USD', + page: route.name, + pageVariantId, + language, + }) + ) + .catch((e) => console.debug(e)); + ``` + with a simplified `pageView` direct call: + ``` + pageView({ + channel: 'WEB', + currency: 'USD', + page: route.name, + pageVariantId, + language, + }).catch((e) => console.debug(e)); + ``` + +* Update `src/byoc/index.ts` to make sure Forms are functioning post-upgrade: + * Rename the file to `index.tsx` + * Remove the context import: + ``` + import { context } from 'lib/context'; + ``` + * Add imports for config and CloudSDK: + ``` + import React from 'react'; + import * as Events from '@sitecore-cloudsdk/events/browser'; + import config from 'temp/config'; + import { + LayoutServicePageState, + SitecoreContextReactContext, + } from '@sitecore-jss/sitecore-jss-nextjs'; + ``` + * Remove the existing `FEAAS.setContextProperties()` call + * Add the component defintion that will hold the updated logic: + ``` + const BYOCInit = (): JSX.Element | null => { + const sitecoreContext = React.useContext(SitecoreContextReactContext).context; + // Set context properties to be available within BYOC components + FEAAS.setContextProperties({ + sitecoreEdgeUrl: config.sitecoreEdgeUrl, + sitecoreEdgeContextId: config.sitecoreEdgeContextId, + pageState: sitecoreContext?.pageState || LayoutServicePageState.Normal, + siteName: sitecoreContext?.site?.name || config.sitecoreSiteName, + eventsSDK: Events, + }); + + return ; + }; + ``` + * Replace the default import at the end of the file with + ``` + export default BYOCInit; + ``` + +* If you have any other instances of using CloudSDK in your app, follow the CloudSDK 0.4.0 upgrade guide. + +* Remove any other `lib/context` import, if present. If you used `context.getSDK()` method, you can now use CloudSDK method calls directly. If `context` was used to retrieve other values, consider using `temp/config` instead. \ No newline at end of file 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 c2d7a5e3aa..a2952f9d48 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,8 @@ { "dependencies": { - "@sitecore/components": "^1.1.10", - "@sitecore-cloudsdk/events": "^0.3.1", + "@sitecore/components": "~2.0.0", + "@sitecore-cloudsdk/core": "^0.4.0", + "@sitecore-cloudsdk/events": "^0.4.0", "@sitecore-feaas/clientside": "^0.5.17" } } 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 e7343b9bc3..c69834458b 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,21 +1,36 @@ +import { useEffect } from 'react'; import { SitecorePageProps } from 'lib/page-props'; -import { context } from 'src/lib/context'; +import { CloudSDK } from '@sitecore-cloudsdk/core/browser'; +import '@sitecore-cloudsdk/events/browser'; import config from 'temp/config'; +import { LayoutServicePageState } from '@sitecore-jss/sitecore-jss-nextjs'; /** * 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 => { - /** - * 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: props.site?.name || config.sitecoreSiteName, - pageState: props.layoutData?.sitecore?.context?.pageState, - }); + // Browser ClientSDK init allows for page view events to be tracked + useEffect(() => { + const pageState = props.layoutData?.sitecore?.context.pageState; + if (process.env.NODE_ENV === 'development') + console.debug('Browser Events SDK is not initialized in development environment'); + else if (pageState !== LayoutServicePageState.Normal) + console.debug('Browser Events SDK is not initialized in edit and preview modes'); + else { + CloudSDK({ + sitecoreEdgeUrl: config.sitecoreEdgeUrl, + sitecoreEdgeContextId: config.sitecoreEdgeContextId, + siteName: props.site?.name || config.sitecoreSiteName, + enableBrowserCookie: true, + // 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\./, ''), + }) + .addEvents() + .initialize(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.site?.name]); return null; }; 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.tsx similarity index 57% rename from packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/index.ts rename to packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/byoc/index.tsx index 3339b869b2..311324be4f 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.tsx @@ -1,26 +1,43 @@ +import React from 'react'; import * as FEAAS from '@sitecore-feaas/clientside/react'; +import * as Events from '@sitecore-cloudsdk/events/browser'; import '@sitecore/components/context'; import dynamic from 'next/dynamic'; -import { context } from 'lib/context'; +import config from 'temp/config'; +import { + LayoutServicePageState, + SitecoreContextReactContext, +} from '@sitecore-jss/sitecore-jss-nextjs'; /** * 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 * It's recommended to not modify this file - please add BYOC imports in corresponding index.*.ts files instead */ -// Set context properties to be available within BYOC components -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'), { ssr: false, }); -// Import your hybrid (server rendering with client hydration) components via index.hybrid.ts -import './index.hybrid'; - // As long as component bundle is exported and rendered on page (as an empty element), client-only BYOC components are registered and become available // The rest of components will be regsitered in both server and client-side contexts when this module is imported into Layout FEAAS.enableNextClientsideComponents(dynamic, ClientBundle); -export default FEAAS.ExternalComponentBundle; +// Import your hybrid (server rendering with client hydration) components via index.hybrid.ts +import './index.hybrid'; + +const BYOCInit = (): JSX.Element | null => { + const sitecoreContext = React.useContext(SitecoreContextReactContext).context; + // Set context properties to be available within BYOC components + FEAAS.setContextProperties({ + sitecoreEdgeUrl: config.sitecoreEdgeUrl, + sitecoreEdgeContextId: config.sitecoreEdgeContextId, + pageState: sitecoreContext?.pageState || LayoutServicePageState.Normal, + siteName: sitecoreContext?.site?.name || config.sitecoreSiteName, + eventsSDK: Events, + }); + + return ; +}; + +export default BYOCInit; diff --git a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/components/CdpPageView.tsx b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/components/CdpPageView.tsx index c02f64d33b..5ae9ac0f9d 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/components/CdpPageView.tsx +++ b/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/components/CdpPageView.tsx @@ -4,8 +4,8 @@ import { useSitecoreContext, } from '@sitecore-jss/sitecore-jss-nextjs'; import { useEffect } from 'react'; +import { pageView } from '@sitecore-cloudsdk/events/browser'; import config from 'temp/config'; -import { context } from 'lib/context'; /** * This is the CDP page view component. @@ -46,19 +46,14 @@ const CdpPageView = (): JSX.Element => { variantId as string, scope ); - // there are cases where Events SDK will be absent which are expected to reject - context - .getSDK('Events') - .then((Events) => - Events.pageView({ - channel: 'WEB', - currency: 'USD', - page: route.name, - pageVariantId, - language, - }) - ) - .catch((e) => console.debug(e)); + // there can be cases where Events are not initialized which are expected to reject + pageView({ + channel: 'WEB', + currency: 'USD', + page: route.name, + pageVariantId, + language, + }).catch((e) => console.debug(e)); }, [pageState, route, variantId, site]); return <>; 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 deleted file mode 100644 index 4323c5ff53..0000000000 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Context } from '@sitecore-jss/sitecore-jss-nextjs/context'; -import config from 'temp/config'; - -import Events from './sdk/events'; - -/** - * List of SDKs to be initialized. - * Each SDK is defined as a module with the @type {SDK} type. - */ -const sdks = { - Events, -}; - -/** - * Context instance that is used to initialize the application Context and associated Software Development Kits (SDKs). - */ -export const context = new Context({ - sitecoreEdgeUrl: config.sitecoreEdgeUrl, - sitecoreEdgeContextId: config.sitecoreEdgeContextId, - siteName: config.sitecoreSiteName, - 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 deleted file mode 100644 index a9faf3d12a..0000000000 --- a/packages/create-sitecore-jss/src/templates/nextjs-xmcloud/src/lib/context/sdk/events.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as Events from '@sitecore-cloudsdk/events/browser'; -import { SDK } from '@sitecore-jss/sitecore-jss-nextjs/context'; - -const sdkModule: SDK = { - sdk: Events, - 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') - throw 'Browser Events SDK is not initialized in server context'; - if (process.env.NODE_ENV === 'development') - throw 'Browser Events SDK is not initialized in development environment'; - - await Events.init({ - siteName: props.siteName, - sitecoreEdgeUrl: props.sitecoreEdgeUrl, - 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 - enableBrowserCookie: true, - }); - }, -}; - -export default sdkModule; diff --git a/packages/sitecore-jss-nextjs/context.d.ts b/packages/sitecore-jss-nextjs/context.d.ts deleted file mode 100644 index 72e501678d..0000000000 --- a/packages/sitecore-jss-nextjs/context.d.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './types/context/index'; diff --git a/packages/sitecore-jss-nextjs/context.js b/packages/sitecore-jss-nextjs/context.js deleted file mode 100644 index e9529d5720..0000000000 --- a/packages/sitecore-jss-nextjs/context.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./dist/cjs/context/index'); diff --git a/packages/sitecore-jss-nextjs/package.json b/packages/sitecore-jss-nextjs/package.json index 30d4e6b2fa..598b7b54be 100644 --- a/packages/sitecore-jss-nextjs/package.json +++ b/packages/sitecore-jss-nextjs/package.json @@ -29,7 +29,8 @@ "url": "https://github.com/sitecore/jss/issues" }, "devDependencies": { - "@sitecore-cloudsdk/personalize": "^0.3.1", + "@sitecore-cloudsdk/core": "^0.4.0", + "@sitecore-cloudsdk/personalize": "^0.4.0", "@types/chai": "^4.3.4", "@types/chai-as-promised": "^7.1.5", "@types/chai-string": "^1.4.2", @@ -65,8 +66,8 @@ "typescript": "~4.9.4" }, "peerDependencies": { - "@sitecore-cloudsdk/events": "^0.3.1", - "@sitecore-cloudsdk/personalize": "^0.3.1", + "@sitecore-cloudsdk/core": "^0.4.0", + "@sitecore-cloudsdk/personalize": "^0.4.0", "next": "^14.2.7", "react": "^18.2.0", "react-dom": "^18.2.0" diff --git a/packages/sitecore-jss-nextjs/src/context/context.test.ts b/packages/sitecore-jss-nextjs/src/context/context.test.ts deleted file mode 100644 index dcade0ff67..0000000000 --- a/packages/sitecore-jss-nextjs/src/context/context.test.ts +++ /dev/null @@ -1,248 +0,0 @@ -/* eslint-disable no-unused-expressions */ -/* eslint-disable dot-notation */ -import sinon from 'sinon'; -import { expect } from 'chai'; -import { Context } from './'; -import { LayoutServicePageState } from '@sitecore-jss/sitecore-jss-react'; - -describe('Context', () => { - const sdks = { - Foo: { - sdk: { foo: true }, - init: () => - new Promise((resolve) => { - setTimeout(() => { - resolve(); - }, 300); - }), - }, - Bar: { - sdk: { bar: true }, - init: () => { - return new Promise((resolve) => { - setTimeout(() => { - resolve(); - }, 500); - }); - }, - }, - }; - - const errorSdk = { - Error: { - sdk: { error: 'yes' }, - init: () => { - return new Promise((_, reject) => { - reject('Cannot init Error'); - }); - }, - }, - }; - - const fooInitSpy = sinon.spy(sdks.Foo, 'init'); - const barInitSpy = sinon.spy(sdks.Bar, 'init'); - - const props = { - sitecoreEdgeUrl: 'https://edgeurl', - sitecoreEdgeContextId: 'contextid', - siteName: '', - sdks, - }; - - afterEach(() => { - fooInitSpy.resetHistory(); - barInitSpy.resetHistory(); - }); - - 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); - }); - - it('should provide all the properties when context instance is merged as an object', () => { - const context = new Context(props); - - const merged = { ...context }; - - expect(merged.sitecoreEdgeUrl).to.equal(props.sitecoreEdgeUrl); - expect(merged.sitecoreEdgeContextId).to.equal(props.sitecoreEdgeContextId); - expect(merged.siteName).to.equal(props.siteName); - expect(merged.getSDK).to.equal(context.getSDK); - expect(merged.isInitialized).to.equal(context.isInitialized); - }); - }); - - 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.sdks.Bar).to.equal(undefined); - expect(context.sdks.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.sdks.Foo).to.deep.equal(sdks.Foo.sdk); - expect(context.sdks.Bar).to.deep.equal(sdks.Bar.sdk); - - done(); - }); - }); - - it('should use normal pageMode when context is initialized with empty props', () => { - const context = new Context(props); - - context.init(); - - expect(context.isInitialized).to.be.true; - expect(context.pageState).to.equal(LayoutServicePageState.Normal); - }); - - 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.sdks.Bar).to.equal(undefined); - expect(context.sdks.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.sdks.Foo).to.deep.equal(sdks.Foo.sdk); - expect(context.sdks.Bar).to.deep.equal(sdks.Bar.sdk); - - done(); - }); - }); - - it('should initialize the context with a different page mode', () => { - const context = new Context(props); - - context.init({ pageState: LayoutServicePageState.Edit }); - - expect(context.isInitialized).to.be.true; - expect(context.pageState).to.equal(LayoutServicePageState.Edit); - }); - - it('should not initialize the context if it is already initialized', () => { - const context = new Context(props); - - context.init({ siteName: 'website-1' }); - - expect(context.isInitialized).to.be.true; - - context.init({ siteName: 'website-2' }); - - expect(context.siteName).to.equal('website-1'); - }); - - it('should not fail context init when an SDK init throws', (done) => { - const localProps = { ...props, sdks: errorSdk }; - const context = new Context(localProps); - try { - context.init(); - } catch (e) { - done(e); - } finally { - done(); - } - }); - - it('should reject when getting SDK that rejected initialization', (done) => { - const localProps = { ...props, sdks: errorSdk }; - const context = new Context(localProps); - context.init(); - context - .getSDK('Error') - .then(() => { - done('should not resolve'); - }) - .catch((e) => { - expect(e).to.be.equal('Cannot init Error'); - done(); - }); - }); - }); - - describe('getSDK', () => { - it('should return the SDKs', (done) => { - const context = new Context(props); - - context.init(); - - expect(context.sdks.Bar).to.equal(undefined); - expect(context.sdks.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.sdks.Foo).to.deep.equal(sdks.Foo.sdk); - expect(context.sdks.Bar).to.deep.equal(sdks.Bar.sdk); - - done(); - }); - }); - - it('should reject unknown SDK', async () => { - const context = new Context(props); - const sdk = 'Baz'; - - context.init(); - - return context - .getSDK(sdk) - .then(() => { - throw new Error('should not resolve'); - }) - .catch((e) => { - expect(e).to.equal(`Unknown SDK '${sdk}'`); - }); - }); - }); -}); diff --git a/packages/sitecore-jss-nextjs/src/context/context.ts b/packages/sitecore-jss-nextjs/src/context/context.ts deleted file mode 100644 index 3e020395d2..0000000000 --- a/packages/sitecore-jss-nextjs/src/context/context.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { LayoutServicePageState } from '@sitecore-jss/sitecore-jss-react'; - -/** - * Software Development Kit (SDK) instance - */ -export type SDK = { - /** - * The Software Development Kit (SDK) library instance - */ - sdk: SDKType; - /** - * Initializes the Software Development Kit (SDK) - */ - init: (props: InitSDKProps) => 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; - /** - * Sitecore page state (normal, preview, edit) - */ - pageState?: LayoutServicePageState; -} - -/** - * Configuration that is passed to the Context. - */ -export interface ContextConfig { - /** - * Your Sitecore Edge URL - */ - sitecoreEdgeUrl: string; - /** - * Your Sitecore Edge Context ID - */ - sitecoreEdgeContextId: string; - /** - * Your Sitecore site name - */ - siteName: string; - /** - * Software Development Kits (SDKs) to be initialized - */ - sdks: { [module in keyof SDKModules]: SDKModules[module] }; -} - -/** - * Properties that are passed to the Software Development Kit (SDK) initialization function. - */ -type InitSDKProps = Omit, 'sdks'>; - -/** - * 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; - /** - * The Sitecore Edge URL - */ - public readonly sitecoreEdgeUrl: string; - /** - * The Sitecore Edge Context ID - */ - public readonly sitecoreEdgeContextId: string; - /** - * The Sitecore site name - */ - public siteName: string; - /** - * Sitecore page state (normal, preview, edit) - */ - public pageState: LayoutServicePageState; - /** - * Software Development Kits (SDKs) to be initialized - */ - public readonly sdks: { [module in keyof SDKModules]?: SDKModules[module]['sdk'] } = {}; - /** - * Promises for the SDKs - */ - protected sdkPromises: { [module in keyof SDKModules]?: Promise } = {}; - - protected sdkErrors: { [module in keyof SDKModules]?: string } = {}; - - constructor(protected props: ContextConfig) { - this.sitecoreEdgeUrl = props.sitecoreEdgeUrl; - this.sitecoreEdgeContextId = props.sitecoreEdgeContextId; - this.siteName = props.siteName; - this.pageState = LayoutServicePageState.Normal; - } - - public init(props: ContextInitProps = {}) { - // Context and SDKs are initialized only once - if (this.isInitialized) return; - - this.isInitialized = true; - - if (props.siteName) { - this.siteName = props.siteName; - } - - if (props.pageState) { - this.pageState = props.pageState; - } - - // iterate over the SDKs and initialize them - for (const sdkName of Object.keys(this.props.sdks) as (keyof SDKModules)[]) { - this.initSDK(sdkName); - } - } - - /** - * Retrieves the Software Development Kit (SDK) instance, ensuring it is initialized before returning - * - * @param {string} name SDK name - * @returns initialized SDK - */ - public getSDK = (name: T): Promise => { - if (!this.sdkPromises[name]) { - return Promise.reject(`Unknown SDK '${String(name)}'`); - } else { - return this.sdkPromises[name]!.then((result) => { - return ( - (this.sdkErrors[name] && Promise.reject(this.sdkErrors[name])) || Promise.resolve(result) - ); - }); - } - }; - - /** - * 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.sdks[name] - .init(this) - .then(() => { - this.sdks[name] = this.props.sdks[name].sdk; - resolve(this.sdks[name]); - }) - .catch((e) => { - // if init rejects, we mark SDK as failed - so getSDK call would reject with a reason - this.sdkErrors[name] = e; - resolve(undefined); - }); - }); - } -} diff --git a/packages/sitecore-jss-nextjs/src/context/index.ts b/packages/sitecore-jss-nextjs/src/context/index.ts deleted file mode 100644 index b7719c3c1b..0000000000 --- a/packages/sitecore-jss-nextjs/src/context/index.ts +++ /dev/null @@ -1 +0,0 @@ -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 82dc99b4c0..a3bb4beee6 100644 --- a/packages/sitecore-jss-nextjs/src/index.ts +++ b/packages/sitecore-jss-nextjs/src/index.ts @@ -128,8 +128,6 @@ export { BYOCWrapper }; export { ComponentBuilder, ComponentBuilderConfig } from './ComponentBuilder'; -export { Context, ContextConfig, SDK } from './context'; - export { ComponentFactory, Image, diff --git a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts index c6977d43e7..3748eb7169 100644 --- a/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts +++ b/packages/sitecore-jss-nextjs/src/middleware/personalize-middleware.ts @@ -9,7 +9,8 @@ import { } from '@sitecore-jss/sitecore-jss/personalize'; import { debug } from '@sitecore-jss/sitecore-jss'; import { MiddlewareBase, MiddlewareBaseConfig } from './middleware'; -import { init, personalize } from '@sitecore-cloudsdk/personalize/server'; +import { CloudSDK } from '@sitecore-cloudsdk/core/server'; +import { personalize } from '@sitecore-cloudsdk/personalize/server'; export type CdpServiceConfig = { /** @@ -119,13 +120,15 @@ export class PersonalizeMiddleware extends MiddlewareBase { request: NextRequest; response: NextResponse; }): Promise { - await init(request, response, { + await CloudSDK(request, response, { sitecoreEdgeUrl: this.config.cdpConfig.sitecoreEdgeUrl, sitecoreEdgeContextId: this.config.cdpConfig.sitecoreEdgeContextId, siteName, cookieDomain: hostname, enableServerCookie: true, - }); + }) + .addPersonalize() + .initialize(); } protected async personalize( diff --git a/yarn.lock b/yarn.lock index 3b74595f40..3f9ed5e25d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5997,30 +5997,41 @@ __metadata: languageName: node linkType: hard -"@sitecore-cloudsdk/core@npm:^0.3.1": - version: 0.3.1 - resolution: "@sitecore-cloudsdk/core@npm:0.3.1" +"@sitecore-cloudsdk/core@npm:^0.4.0": + version: 0.4.0 + resolution: "@sitecore-cloudsdk/core@npm:0.4.0" dependencies: - "@sitecore-cloudsdk/utils": ^0.3.1 + "@sitecore-cloudsdk/utils": ^0.4.0 debug: ^4.3.4 - checksum: c19d750b1c194f7575d20b3d46739d7b41545087a9fb794ba44ab9b2d812f2b2035caf19c4f4ba51e6ff628c99f2e4a18b0a7a2578b413c7adb04f3d77f392a3 + checksum: 0640147f4767c6d84f90bbefe011fcc76d2fb47adc078f5017f7c2d686c7eff53a661cd77c4b70315c06b2be0b071c8d279a0db3502bcf5e6e288d1a36dd8896 languageName: node linkType: hard -"@sitecore-cloudsdk/personalize@npm:^0.3.1": - version: 0.3.1 - resolution: "@sitecore-cloudsdk/personalize@npm:0.3.1" +"@sitecore-cloudsdk/events@npm:^0.4.0": + version: 0.4.0 + resolution: "@sitecore-cloudsdk/events@npm:0.4.0" dependencies: - "@sitecore-cloudsdk/core": ^0.3.1 - "@sitecore-cloudsdk/utils": ^0.3.1 - checksum: e48b1429f67be22069bfd8cc6349660ed346877fef5537def5f36c962fd2264969ff02fb2e16333dabc683c4dd0373ae12ce56b97a6850910ab618957b9586d9 + "@sitecore-cloudsdk/core": ^0.4.0 + "@sitecore-cloudsdk/utils": ^0.4.0 + checksum: 0e36c44b4bfd23e71b46f20d9ef852e8a948da3ff9f037e37b8a68f4c36220f48d44c2a032979855a5945ed0b100272f7a477ea1f7fdc6b77eab4a32a1d22657 languageName: node linkType: hard -"@sitecore-cloudsdk/utils@npm:^0.3.1": - version: 0.3.1 - resolution: "@sitecore-cloudsdk/utils@npm:0.3.1" - checksum: c6578ec04823a77aaf7cb6f9cdb842c227698507c38b722db0b9fe1c6b21ee169443cc6b9b8ca77f86c9a4dfc835e1c3b85369079c70ef88e555c8243275d9a6 +"@sitecore-cloudsdk/personalize@npm:^0.4.0": + version: 0.4.0 + resolution: "@sitecore-cloudsdk/personalize@npm:0.4.0" + dependencies: + "@sitecore-cloudsdk/core": ^0.4.0 + "@sitecore-cloudsdk/events": ^0.4.0 + "@sitecore-cloudsdk/utils": ^0.4.0 + checksum: 62668f95a111c1b25668c20fc7cf0bf1958b3aefaf2b9c6cdb79179de4e508b7b466e25654f8efcaefdbffd86a715f210e76f2fda964650f601b0fc9e0579893 + languageName: node + linkType: hard + +"@sitecore-cloudsdk/utils@npm:^0.4.0": + version: 0.4.0 + resolution: "@sitecore-cloudsdk/utils@npm:0.4.0" + checksum: ec5a0611ad4d6ae4b3c2a7542c2021f0b956d2c78e08cab1ff8bd5e6300c3059d5fff6022d0ed2462cd0b4f127d02b5bfabf34e890cddbb51876aebaca3033dc languageName: node linkType: hard @@ -6213,7 +6224,8 @@ __metadata: version: 0.0.0-use.local resolution: "@sitecore-jss/sitecore-jss-nextjs@workspace:packages/sitecore-jss-nextjs" dependencies: - "@sitecore-cloudsdk/personalize": ^0.3.1 + "@sitecore-cloudsdk/core": ^0.4.0 + "@sitecore-cloudsdk/personalize": ^0.4.0 "@sitecore-jss/sitecore-jss": 22.2.0-canary.82 "@sitecore-jss/sitecore-jss-dev-tools": 22.2.0-canary.82 "@sitecore-jss/sitecore-jss-react": 22.2.0-canary.82 @@ -6255,8 +6267,8 @@ __metadata: ts-node: ^10.9.1 typescript: ~4.9.4 peerDependencies: - "@sitecore-cloudsdk/events": ^0.3.1 - "@sitecore-cloudsdk/personalize": ^0.3.1 + "@sitecore-cloudsdk/core": ^0.4.0 + "@sitecore-cloudsdk/personalize": ^0.4.0 next: ^14.2.7 react: ^18.2.0 react-dom: ^18.2.0