Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Nextjs] CloudSDK Integration #1652

Merged
merged 43 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
31adef9
Initial commit
illiakovalenko Nov 2, 2023
a4a9a56
Merge branch 'dev' of https://github.com/Sitecore/jss into feature/JS…
illiakovalenko Nov 2, 2023
6fe1dcf
Handle async Events init call
illiakovalenko Nov 2, 2023
8672755
Update
illiakovalenko Nov 3, 2023
890922a
Update
illiakovalenko Nov 3, 2023
f6bc981
Updated test
illiakovalenko Nov 3, 2023
0fe146c
rename nextjs-personalize -> nextjs-xmcloud. move feaas and BYOC here.
ambrauer Nov 3, 2023
a2dfa99
Move Sitecore Edge Platform / conext related items to nextjs-xmcloud
ambrauer Nov 3, 2023
4cbe740
Updated context initialization
illiakovalenko Nov 6, 2023
3d64d0a
Updated dependency
illiakovalenko Nov 6, 2023
657b08e
Updated Yarn.lock
illiakovalenko Nov 6, 2023
ee929de
Merge branch 'dev' of https://github.com/Sitecore/jss into feature/JS…
illiakovalenko Nov 6, 2023
244fa5b
Fix lint errors
illiakovalenko Nov 6, 2023
2c95fd0
Updated CHANGELOG
illiakovalenko Nov 6, 2023
2df0a00
Repurpose nextjs-personalize -> nextjs-xmcloud initializer "system" t…
ambrauer Nov 6, 2023
1e8f7b4
Moved skipping of site information fetch on XM Cloud to base package …
ambrauer Nov 6, 2023
bf35161
CHANGELOG update
ambrauer Nov 6, 2023
b79b198
Updated Context implementation, added unit tests
illiakovalenko Nov 7, 2023
22deb3e
Updated CHANGELOG
illiakovalenko Nov 7, 2023
6349af7
Updated comment
illiakovalenko Nov 7, 2023
49af700
Updated cloudsdk to use latest production version
illiakovalenko Nov 7, 2023
9e8d7f5
Updated yarn.lock
illiakovalenko Nov 7, 2023
358dd37
Merge branch 'feature/JSS-1117-nextjs-xmcloud-template' of https://gi…
illiakovalenko Nov 7, 2023
40933f0
Introduced Bootstrap and pulled nextjs-xmcloud
illiakovalenko Nov 7, 2023
a013dc1
Merge branch 'dev' of https://github.com/Sitecore/jss into feature/JS…
illiakovalenko Nov 7, 2023
0e8e5f1
Update .env
illiakovalenko Nov 7, 2023
47a5c7d
Updated jsdoc
illiakovalenko Nov 7, 2023
e7ec8f8
Avoid unused vars rule for Bootstrap
illiakovalenko Nov 7, 2023
7769bfb
Normalize sitecoreEdgeUrl
illiakovalenko Nov 7, 2023
311c820
Updated CHANGELOG
illiakovalenko Nov 7, 2023
dc70eb7
Updated BYOC initialization
illiakovalenko Nov 7, 2023
e1bb785
Updated Props doc
illiakovalenko Nov 7, 2023
2d8633b
Updates
illiakovalenko Nov 8, 2023
924cb40
Updated unit test
illiakovalenko Nov 8, 2023
2ee000b
Updated comment
illiakovalenko Nov 8, 2023
4315eab
Provided sitecoreEdgeUrl to the middleware
illiakovalenko Nov 8, 2023
8503b16
Merge branch 'dev' of https://github.com/Sitecore/jss into feature/JS…
illiakovalenko Nov 8, 2023
5e60944
Refactoring
illiakovalenko Nov 8, 2023
d08ba22
Passing props to SDK's
illiakovalenko Nov 8, 2023
3712ea5
Updated type
illiakovalenko Nov 8, 2023
b2ef26e
Merge branch 'dev' of https://github.com/Sitecore/jss into feature/JS…
illiakovalenko Nov 8, 2023
defd3f3
Renamed SDKs -> sdks
illiakovalenko Nov 8, 2023
72c3c23
Updated yarn.lock
illiakovalenko Nov 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ 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))
* `[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 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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
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';

function App({ Component, pageProps }: AppProps<SitecorePageProps>): 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.
<I18nProvider lngDict={dictionary} locale={pageProps.locale}>
<Component {...rest} />
</I18nProvider>
<>
<Bootstrap {...pageProps} />
{/*
// 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.
*/}
<I18nProvider lngDict={dictionary} locale={pageProps.locale}>
<Component {...rest} />
</I18nProvider>
</>
);
}

Expand Down
10 changes: 0 additions & 10 deletions packages/create-sitecore-jss/src/templates/nextjs-xmcloud/.env
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,11 @@ SITECORE_EDGE_CONTEXT_ID=

# ==============================================

# 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=

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"dependencies": {
"@sitecore/components": "~1.0.19",
illiakovalenko marked this conversation as resolved.
Show resolved Hide resolved
"@sitecore/engage": "^1.4.1",
"@sitecore-cloudsdk/events": "^0.1.1",
"@sitecore-feaas/clientside": "^0.4.12"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { SitecorePageProps } from 'lib/page-props';
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 value for the app
initContext({ siteName: siteInfo.name });

return null;
};

export default Bootstrap;
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import * as FEAAS from '@sitecore-feaas/clientside/react';
import dynamic from 'next/dynamic';
import config from 'temp/config';
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
* 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({
sitecoreEdgeUrl: config.sitecoreEdgeUrl,
sitecoreEdgeContextId: config.sitecoreEdgeContextId,
});
FEAAS.setContextProperties(contextState);
illiakovalenko marked this conversation as resolved.
Show resolved Hide resolved

// 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'), {
Expand Down
Original file line number Diff line number Diff line change
@@ -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 { context } from 'lib/context';

/**
* 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/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.
Expand All @@ -68,7 +37,6 @@ const CdpPageView = (): JSX.Element => {
return;
}

const siteInfo = siteResolver.getByName(site?.name || config.siteName);
const language = route.itemLanguage || config.defaultLanguage;
const scope = process.env.NEXT_PUBLIC_PERSONALIZE_SCOPE;

Expand All @@ -78,7 +46,16 @@ const CdpPageView = (): JSX.Element => {
variantId as string,
scope
);
createPageView(route.name, language, siteInfo, pageVariantId);

context.getSDK('Events')?.then((Events) =>
Events.pageView({
channel: 'WEB',
currency: 'USD',
page: route.name,
pageVariantId,
language,
})
);
}, [pageState, route, variantId, site]);

return <></>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
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<SDKType> = {
sdk: SDKType;
init: () => Promise<void>;
};

/**
* 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'] };

/**
* Initial properties for the application Context initialization.
*/
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<SDKs>({
sitecoreEdgeUrl: config.sitecoreEdgeUrl,
sitecoreEdgeContextId: config.sitecoreEdgeContextId,
});

/**
* The application Context State
*/
export let contextState = context.getState();
illiakovalenko marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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 and SDKs are initialized only once
if (context.isInitialized) return;
illiakovalenko marked this conversation as resolved.
Show resolved Hide resolved

context.isInitialized = true;

// Updating the context with the incoming properties
contextState = context.getState({
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();

return modules[sdkName].sdk;
});
}

// Setting the context properties for the FEAAS SDK
FEAAS.setContextProperties(context);
illiakovalenko marked this conversation as resolved.
Show resolved Hide resolved
};
Original file line number Diff line number Diff line change
@@ -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,
illiakovalenko marked this conversation as resolved.
Show resolved Hide resolved
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<typeof sdk> = {
sdk,
init,
};

export default sdkModule;
Original file line number Diff line number Diff line change
Expand Up @@ -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';

/**
Expand Down Expand Up @@ -33,8 +34,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 || '',
sitecoreEdgeContextId: config.sitecoreEdgeContextId,
timeout:
(process.env.PERSONALIZE_MIDDLEWARE_CDP_TIMEOUT &&
parseInt(process.env.PERSONALIZE_MIDDLEWARE_CDP_TIMEOUT)) ||
Expand All @@ -50,9 +50,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) => { ... }
ambrauer marked this conversation as resolved.
Show resolved Hide resolved
});
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
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;
};

export default Bootstrap;
Loading