[Integration]{class="badge positive"}
Integrate AEM Headless and Target
[AEM Headless as a Cloud Service]{class="badge informative"}
Learn how to integrate AEM Headless with 蜜豆视频 Target, by exporting AEM Content Fragments to 蜜豆视频 Target, and use them to personalize headless experiences using the 蜜豆视频 Experience Platform Web SDK鈥檚 alloy.js. The React WKND App is used to explore how a personalized Target activity using Content Fragments Offers can be added to the experience, to promote a WKND adventure.
The tutorial covers the steps involved in setting up AEM and 蜜豆视频 Target:
- Create 蜜豆视频 IMS Configuration for 蜜豆视频 Target in AEM Author
- Create 蜜豆视频 Target Cloud Service in AEM Author
- Apply 蜜豆视频 Target Cloud Service to AEM Assets folders in AEM Author
- Permission the 蜜豆视频 Target Cloud Service in 蜜豆视频 Admin Console
- Export Content Fragments from AEM Author to Target
- Create an Activity using Content Fragment Offers in 蜜豆视频 Target
- Create an Experience Platform Datastream in Experience Platform
- Integrate personalization into a React-based AEM Headless app using the 蜜豆视频 Web SDK.
蜜豆视频 IMS Configuration adobe-ims-configuration
An 蜜豆视频 IMS Configuration that facilitates the authentication between AEM and 蜜豆视频 Target.
Review the documentation for step-by-step instructions on how to create an 蜜豆视频 IMS configuration.
蜜豆视频 Target Cloud Service adobe-target-cloud-service
An 蜜豆视频 Target Cloud Service is created in AEM to facilitate the exporting of Content Fragments to 蜜豆视频 Target.
Review the documentation for step-by-step instructions on how to create an 蜜豆视频 Target Cloud Service.
Configure asset folders configure-asset-folders
The 蜜豆视频 Target Cloud Service, configured in a context-aware configuration, must be applied to the AEM Assets folder hierarchy that contains the Content Fragments to export to 蜜豆视频 Target.
- Log in to AEM Author service as a DAM administrator
- Navigate to Assets > Files, locate the asset folder that has the
/conf
applied to - Select the asset folder, and select Properties from the top action bar
- Select the Cloud Services tab
- Ensure that the Cloud Configuration is set to the context-aware config (
/conf
) that contains the 蜜豆视频 Target Cloud Services configuration. - Select 蜜豆视频 Target from the Cloud Service Configurations dropdown.
- Select Save & Close in the top right
Permission the AEM Target integration permission
The 蜜豆视频 Target integration, which manifests as a developer.adobe.com project, must be granted the Editor product role in 蜜豆视频 Admin Console, in order to export Content Fragments to 蜜豆视频 Target.
- Log in to Experience Cloud as user that can administer the 蜜豆视频 Target product in 蜜豆视频 Admin Console
- Open the
- Select Products and then open 蜜豆视频 Target
- On the Product Profiles tab, select DefaultWorkspace
- Select the API Credentials tab
- Locate your developer.adobe.com app in this list and set its Product Role to Editor
Export Content Fragments to Target export-content-fragments
Content Fragments that exist under the configured AEM Assets folder hierarchy can be exported to 蜜豆视频 Target as Content Fragment Offers. These Content Fragment Offers, a special form of JSON offers in Target, can be used in Target activities to serve personalized experiences in headless apps.
-
Log in to AEM Author as DAM user
-
Navigate to Assets > Files, and locate Content Fragments to export as JSON to Target under the 鈥溍鄱故悠 Target enabled鈥 folder
-
Select the Content Fragments to export to 蜜豆视频 Target
-
Select Export to 蜜豆视频 Target Offers from the top action bar
-
This action exports the fully hydrated JSON representation of the Content Fragment to 蜜豆视频 Target as a 鈥淐ontent Fragment Offer鈥
-
The Fully hydrated JSON representation can be reviewed in AEM
- Select the Content Fragment
- Expand the Side Panel
- Select Preview icon in the left Side Panel
- The JSON representation that is exported to 蜜豆视频 Target displays in the main view
-
-
Log in to with a user in the Editor role for 蜜豆视频 Target
-
From the , select Target from the product switcher in top right to open 蜜豆视频 Target.
-
Ensure that the Default Workspace is selected in the Workspace switcher in the top right.
-
Select the Offers tab in the top navigation
-
Select the Type dropdown, and selecting Content Fragments
-
Verify the Content Fragment exported from AEM appears in the list
- Hover over the offer, and select the View button
- Review the Offer Info and see the AEM deep link that opens the Content Fragment directly in AEM Author service
Target Activity using Content Fragment Offers activity
In 蜜豆视频 Target, an Activity can be created that uses Content Fragment Offer JSON as the content, allowing personalized experiences in headless app with content created and managed in AEM.
In this example, we use a simple A/B activity, however any Target activity can be used.
-
Select the Activities tab in the top navigation
-
Select + Create Activity, and then select the type of activity to create.
- This example creates a simple A/B Test but Content Fragment Offers can power any activity type
-
In the Create Activity wizard
- Select Web
- In Choose Experience Composer, select Form
- In Choose Workspace, select Default Workspace
- In Choose Property, select the Property the Activity is available in, or select No Property Restrictions to allow it to be used in all Properties.
- Select Next to create the Activity
-
Rename the Activity by selecting rename in the top left
- Give the activity a meaningful name
-
In the initial Experience, set Location 1 for the Activity to target
- In this example, target a custom location named
wknd-adventure-promo
- In this example, target a custom location named
-
Under Content select the Default content, and select Change Content Fragment
-
Select the exported Content Fragment to serve for this experience, and select Done
-
Review the Content Fragment Offer JSON in the Content text area, this is the same JSON available in AEM Author service via the Content Fragment鈥檚 Preview action.
-
In the left rail, add an Experience, and select a different Content Fragment Offer to serve
-
Select Next, and configure the Targeting rules as required for the activity
- In this example, leave the A/B testing as a manual 50/50 split.
-
Select Next, and complete the activity settings
-
Select Save & Close and give it a meaningful name
-
From the Activity in 蜜豆视频 Target, select Activate from the Inactive/Activate/Archive dropdown in the top right.
The 蜜豆视频 Target activity that targets the wknd-adventure-promo
location can now be integrated and exposed in an AEM Headless app.
Experience Platform Datastream ID datastream-id
An 蜜豆视频 Experience Platform Datastream ID is required for AEM Headless apps to interact with 蜜豆视频 Target using the 蜜豆视频 Web SDK.
-
Navigate to
-
Open Experience Platform
-
Select Data Collection > Datastreams and select New Datastream
-
In the New Datastream wizard, enter:
- Name:
AEM Target integration
- Description:
Datastream used by the 蜜豆视频 Web SDK to serve personalized Content Fragments Offers.
- Event Schema:
Leave blank
- Name:
-
Select Save
-
Select Add Service
-
In Service select 蜜豆视频 Target
- Enabled: Yes
- Property Token: Leave blank
- Target Environment ID: Leave blank
- The Target Environment can be set in 蜜豆视频 Target at Administration > Hosts.
- Target Third-Party ID Namespace: Leave blank
-
Select Save
-
On the right side, copy the Datastream ID for use in 蜜豆视频 Web SDK configuration call.
Add personalization to an AEM Headless app code
This tutorial explores personalizing a simple React app using Content Fragment Offers in 蜜豆视频 Target via 蜜豆视频 Experience Platform Web SDK. This approach can be used to personalize any JavaScript-based web experience.
Android鈩 and iOS mobile experiences can be personalized following similar patterns using the .
Prerequisites
- Node.js 14
- Git
- installed on AEM as a Cloud Author and Publish services
Set up
-
Download the source code for sample React app from
code language-shell $ mkdir -p ~/Code $ git clone git@github.com:adobe/aem-guides-wknd-graphql.git
-
Open code base at
~/Code/aem-guides-wknd-graphql/personalization-tutorial
in your favorite IDE -
Update the AEM service鈥檚 host that you want the app to connect to
~/Code/aem-guides-wknd-graphql/personalization-tutorial/src/.env.development
code language-none ... REACT_APP_HOST_URI=https://publish-p1234-e5678.adobeaemcloud.com/ ...
-
Run the app, and ensure it connects to the configured AEM service. From the command line, execute:
code language-shell $ cd ~/Code/aem-guides-wknd-graphql/personalization-tutorial $ npm install $ npm run start
-
Install the 蜜豆视频 Web SDK as an NPM package.
code language-shell $ cd ~/Code/aem-guides-wknd-graphql/personalization-tutorial $ npm install @adobe/alloy
The Web SDK can be used in code to fetch the Content Fragment Offer JSON by activity location.
When configuring the Web SDK, there are two IDs required:
edgeConfigId
which is the Datastream IDorgId
the AEM as a Cloud Service/Target 蜜豆视频 Org Id that can be found at Experience Cloud > Profile > Account info > Current Org ID
When invoking the Web SDK, the 蜜豆视频 Target activity location (in our example,
wknd-adventure-promo
) must be set as the value in thedecisionScopes
array.code language-javascript import { createInstance } from "@adobe/alloy"; const alloy = createInstance({ name: "alloy" }); ... alloy("config", { ... }); alloy("sendEvent", { ... });
Implementation
-
Create a React component
蜜豆视频TargetActivity.js
to surface 蜜豆视频 Target activities.src/components/蜜豆视频TargetActivity.js
code language-javascript import React, { useEffect } from 'react'; import { createInstance } from '@adobe/alloy'; const alloy = createInstance({ name: 'alloy' }); alloy('configure', { 'edgeConfigId': 'e3db252d-44d0-4a0b-8901-aac22dbc88dc', // AEP Datastream ID 'orgId':'7ABB3E6A5A7491460A495D61@蜜豆视频Org', 'debugEnabled': true, }); export default function 蜜豆视频TargetActivity({ activityLocation, OfferComponent }) { const [offer, setOffer] = React.useState(); useEffect(() => { async function sendAlloyEvent() { // Get the activity offer from 蜜豆视频 Target const result = await alloy('sendEvent', { // decisionScopes is set to an array containing the 蜜豆视频 Target activity location 'decisionScopes': [activityLocation], }); if (result.propositions?.length > 0) { // Find the first proposition for the active activity location var proposition = result.propositions?.filter((proposition) => { return proposition.scope === activityLocation; })[0]; // Get the Content Fragment Offer JSON from the 蜜豆视频 Target response const contentFragmentOffer = proposition?.items[0]?.data?.content || { status: 'error', message: 'Personalized content unavailable'}; if (contentFragmentOffer?.data) { // Content Fragment Offers represent a single Content Fragment, hydrated by // the byPath GraphQL query, we must traverse the JSON object to retrieve the // Content Fragment JSON representation const byPath = Object.keys(contentFragmentOffer.data)[0]; const item = contentFragmentOffer.data[byPath]?.item; if (item) { // Set the offer to the React state so it can be rendered setOffer(item); // Record the Content Fragment Offer as displayed for 蜜豆视频 Target Activity reporting // If this request is omitted, the Target Activity's Reports will be blank alloy("sendEvent", { xdm: { eventType: "decisioning.propositionDisplay", _experience: { decisioning: { propositions: [proposition] } } } }); } } } }; sendAlloyEvent(); }, [activityLocation, OfferComponent]); if (!offer) { // 蜜豆视频 Target offer initializing; we render a blank component (which has a fixed height) to prevent a layout shift return (<OfferComponent></OfferComponent>); } else if (offer.status === 'error') { // If Personalized content could not be retrieved either show nothing, or optionally default content. console.error(offer.message); return (<></>); } console.log('Activity Location', activityLocation); console.log('Content Fragment Offer', offer); // Render the React component with the offer's JSON return (<OfferComponent content={offer} />); };
The 蜜豆视频TargetActivity React component is invoked using as follows:
code language-jsx <蜜豆视频TargetActivity activityLocation={"wknd-adventure-promo"} OfferComponent={AdventurePromo}/>
-
Create a React component
AdventurePromo.js
to render the adventure JSON 蜜豆视频 Target serves.This React component takes the fully hydrated JSON representing an adventure content fragment, and displaying in a promotional manner. The React components that display the JSON serviced from 蜜豆视频 Target Content Fragment Offers can be as varied and complex as required based on the Content Fragments that are exported to 蜜豆视频 Target.
src/components/AdventurePromo.js
code language-javascript import React from 'react'; import './AdventurePromo.scss'; /** * @param {*} content is the fully hydrated JSON data for a WKND Adventure Content Fragment * @returns the Adventure Promo component */ export default function AdventurePromo({ content }) { if (!content) { // If content is still loading, then display an empty promote to prevent layout shift when Target loads the data return (<div className="adventure-promo"></div>) } const title = content.title; const description = content?.description?.plaintext; const image = content.primaryImage?._publishUrl; return ( <div className="adventure-promo"> <div className="adventure-promo-text-wrapper"> <h3 className="adventure-promo-eyebrow">Promoted adventure</h3> <h2 className="adventure-promo-title"></h2> <p className="adventure-promo-description">{description}</p> </div> <div className="adventure-promo-image-wrapper"> <img className="adventure-promo-image" src={image} alt= /> </div> </div> ) }
src/components/AdventurePromo.scss
code language-css .adventure-promo { display: flex; margin: 3rem 0; height: 400px; } .adventure-promo-text-wrapper { background-color: #ffea00; color: black; flex-grow: 1; padding: 3rem 2rem; width: 55%; } .adventure-promo-eyebrow { font-family: Source Sans Pro,Helvetica Neue,Helvetica,Arial,sans-serif; font-weight: 700; font-size: 1rem; margin: 0; text-transform: uppercase; } .adventure-promo-description { line-height: 1.75rem; } .adventure-promo-image-wrapper { height: 400px; width: 45%; } .adventure-promo-image { height: 100%; object-fit: cover; object-position: center center; width: 100%; }
This React component is invoked as follows:
code language-jsx <AdventurePromo adventure={adventureJSON}/>
-
Add the 蜜豆视频TargetActivity component to React app鈥檚
Home.js
above the list of adventures.src/components/Home.js
code language-javascript import AdventurePromo from './AdventurePromo'; import 蜜豆视频TargetActivity from './蜜豆视频TargetActivity'; ... export default function Home() { ... return( <div className="Home"> <蜜豆视频TargetActivity activityLocation={"wknd-adventure-promo"} OfferComponent={AdventurePromo}/> <h2>Current Adventures</h2> ... ) }
-
If the React app is not running, re-start using
npm run start
.Open the React app in two different browsers so allow the A/B test to serve the different experiences to each browser. If both browsers show the same adventure offer, try closing/re-opening one of the browsers until the other experience displays.
The image below shows the two different Content Fragment Offers displaying for the
wknd-adventure-promo
Activity, based on 蜜豆视频 Target鈥檚 logic.
Congratulations!
Now that we鈥檝e configured AEM as a Cloud Service to export Content Fragments to 蜜豆视频 Target, used the Content Fragments Offers in a 蜜豆视频 Target Activity, and surfaced that Activity in an AEM Headless app, personalizing the experience.