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

chore: update the experimentation plugin to the latest version #48

Merged
merged 5 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# AEM Experience Decisioning
# AEM Edge Delivery Services Experimentation

The AEM Experience Decisioning plugin helps you quickly set up experimentation and segmentation on your AEM project.
The AEM Experimentation plugin helps you quickly set up experimentation and segmentation on your AEM project.
It is currently available to customers in collaboration with AEM Engineering via co-innovation VIP Projects.
To implement experimentation or personalization use-cases, please reach out to the AEM Engineering team in the Slack channel dedicated to your project.

## Features

The AEM Experience Decisioning plugin supports:
The AEM Experimentation plugin supports:
- :busts_in_silhouette: serving different content variations to different audiences, including custom audience definitions for your project that can be either resolved directly in-browser or against a trusted backend API.
- :money_with_wings: serving different content variations based on marketing campaigns you are running, so that you can easily track email and/or social campaigns
- :chart_with_upwards_trend: running A/B test experiments on a set of variants to measure and improve the conversion on your site. This works particularly with our :chart: [RUM conversion tracking plugin](https://github.com/adobe/franklin-rum-conversion).
Expand All @@ -16,15 +16,15 @@ The AEM Experience Decisioning plugin supports:

Add the plugin to your AEM project by running:
```sh
git subtree add --squash --prefix plugins/experience-decisioning git@github.com:adobe/aem-experience-decisioning.git main
git subtree add --squash --prefix plugins/experimentation git@github.com:adobe/aem-experimentation.git main
```

If you later want to pull the latest changes and update your local copy of the plugin
```sh
git subtree pull --squash --prefix plugins/experience-decisioning git@github.com:adobe/aem-experience-decisioning.git main
git subtree pull --squash --prefix plugins/experimentation git@github.com:adobe/aem-experimentation.git main
```

If you prefer using `https` links you'd replace `git@github.com:adobe/aem-experience-decisioning.git` in the above commands by `https://github.com/adobe/aem-experience-decisioning.git`.
If you prefer using `https` links you'd replace `git@github.com:adobe/aem-experimentation.git` in the above commands by `https://github.com/adobe/aem-experimentation.git`.

## Project instrumentation

Expand All @@ -33,7 +33,7 @@ If you prefer using `https` links you'd replace `git@github.com:adobe/aem-experi
The easiest way to add the plugin is if your project is set up with the plugin system extension in the boilerplate.
You'll know you have it if `window.hlx.plugins` is defined on your page.

If you don't have it, you can follow the proposal in https://github.com/adobe/aem-lib/pull/23 and apply the changes to your `aem.js`/`lib-franklin.js`.
If you don't have it, you can follow the proposal in https://github.com/adobe/aem-lib/pull/23 and https://github.com/adobe/aem-boilerplate/pull/275 and apply the changes to your `aem.js`/`lib-franklin.js` and `scripts.js`.

Once you have confirmed this, you'll need to edit your `scripts.js` in your AEM project and add the following at the start of the file:
```js
Expand All @@ -43,13 +43,12 @@ const AUDIENCES = {
// define your custom audiences here as needed
};

window.hlx.plugins.add('experience-decisioning', {
window.hlx.plugins.add('experimentation', {
condition: () => getMetadata('experiment')
|| Object.keys(getAllMetadata('campaign')).length
|| Object.keys(getAllMetadata('audience')).length,
options: { audiences: AUDIENCES },
load: 'eager',
url: '/plugins/experience-decisioning/src/index.js',
url: '/plugins/experimentation/src/index.js',
});
```

Expand Down Expand Up @@ -103,7 +102,7 @@ To properly connect and configure the plugin for your project, you'll need to ed
|| Object.keys(getAllMetadata('campaign')).length
|| Object.keys(getAllMetadata('audience')).length) {
// eslint-disable-next-line import/no-relative-packages
const { loadEager: runEager } = await import('../plugins/experience-decisioning/src/index.js');
const { loadEager: runEager } = await import('../plugins/experimentation/src/index.js');
await runEager(document, { audiences: AUDIENCES }, pluginContext);
}
Expand All @@ -119,7 +118,7 @@ To properly connect and configure the plugin for your project, you'll need to ed
|| Object.keys(getAllMetadata('campaign')).length
|| Object.keys(getAllMetadata('audience')).length)) {
// eslint-disable-next-line import/no-relative-packages
const { loadLazy: runLazy } = await import('../plugins/experience-decisioning/src/index.js');
const { loadLazy: runLazy } = await import('../plugins/experimentation/src/index.js');
await runLazy(document, { audiences: AUDIENCES }, pluginContext);
}
}
Expand Down Expand Up @@ -168,6 +167,6 @@ runEager.call(pluginContext, {
```

For detailed implementation instructions on the different features, please read the dedicated pages we have on those topics:
- [Audiences](https://github.com/adobe/aem-experience-decisioning/wiki/Audiences)
- [Campaigns](https://github.com/adobe/aem-experience-decisioning/wiki/Campaigns)
- [Experiments](https://github.com/adobe/aem-experience-decisioning/wiki/Experiments)
- [Audiences](https://github.com/adobe/aem-experimentation/wiki/Audiences)
- [Campaigns](https://github.com/adobe/aem-experimentation/wiki/Campaigns)
- [Experiments](https://github.com/adobe/aem-experimentation/wiki/Experiments)

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@adobe/aem-experience-decisioning",
"name": "@adobe/aem-experimentation",
"version": "1.0.0",
"main": "src/index.js",
"scripts": {
Expand All @@ -9,7 +9,7 @@
},
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/adobe/aem-experience-decisioning.git"
"url": "git+ssh://git@github.com/adobe/aem-experimentation.git"
},
"author": "Adobe Inc.",
"license": "Apache-2.0",
Expand All @@ -23,9 +23,9 @@
"audiences"
],
"bugs": {
"url": "https://github.com/adobe/aem-experience-decisioning/issues"
"url": "https://github.com/adobe/aem-experimentation/issues"
},
"homepage": "https://github.com/adobe/aem-experience-decisioning#readme",
"homepage": "https://github.com/adobe/aem-experimentation#readme",
"devDependencies": {
"@babel/eslint-parser": "7.22.15",
"eslint": "8.48.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export async function getResolvedAudiences(applicableAudiences, options, context
* Replaces element with content from path
* @param {string} path
* @param {HTMLElement} element
* @param {boolean} isBlock
* @return Returns the path that was loaded or null if the loading failed
*/
async function replaceInner(path, element) {
const plainPath = path.endsWith('/')
Expand All @@ -92,12 +92,12 @@ async function replaceInner(path, element) {
const html = await resp.text();
// eslint-disable-next-line no-param-reassign
element.innerHTML = html;
return true;
return plainPath;
} catch (e) {
// eslint-disable-next-line no-console
console.log(`error loading content: ${plainPath}`, e);
}
return false;
return null;
}

/**
Expand Down Expand Up @@ -223,7 +223,7 @@ function inferEmptyPercentageSplits(variants) {
* @param {string} instantExperiment The list of varaints
* @returns {object} the experiment manifest
*/
export function getConfigForInstantExperiment(
function getConfigForInstantExperiment(
experimentId,
instantExperiment,
pluginOptions,
Expand Down Expand Up @@ -287,8 +287,15 @@ export function getConfigForInstantExperiment(
* @param {object} pluginOptions The plugin options
* @returns {object} containing the experiment manifest
*/
export async function getConfigForFullExperiment(experimentId, pluginOptions, context) {
const path = `${pluginOptions.experimentsRoot}/${experimentId}/${pluginOptions.experimentsConfigFile}`;
async function getConfigForFullExperiment(experimentId, pluginOptions, context) {
let path;
if (experimentId.includes(`/${pluginOptions.experimentsConfigFile}`)) {
path = new URL(experimentId, window.location.origin).href;
// eslint-disable-next-line no-param-reassign
[experimentId] = path.split('/').splice(-2, 1);
} else {
path = `${pluginOptions.experimentsRoot}/${experimentId}/${pluginOptions.experimentsConfigFile}`;
}
try {
const resp = await fetch(path);
if (!resp.ok) {
Expand Down Expand Up @@ -336,7 +343,7 @@ function getDecisionPolicy(config) {
return decisionPolicy;
}

export async function getConfig(experiment, instantExperiment, pluginOptions, context) {
async function getConfig(experiment, instantExperiment, pluginOptions, context) {
const usp = new URLSearchParams(window.location.search);
const [forcedExperiment, forcedVariant] = usp.has(pluginOptions.experimentsQueryParameter)
? usp.get(pluginOptions.experimentsQueryParameter).split('/')
Expand All @@ -357,7 +364,7 @@ export async function getConfig(experiment, instantExperiment, pluginOptions, co
: null;

experimentConfig.resolvedAudiences = await getResolvedAudiences(
experimentConfig.audiences,
experimentConfig.audiences.map(context.toClassName),
pluginOptions,
context,
);
Expand All @@ -371,9 +378,6 @@ export async function getConfig(experiment, instantExperiment, pluginOptions, co
);

window.hlx = window.hlx || {};
if (!experimentConfig.run) {
return false;
}
window.hlx.experiment = experimentConfig;

// eslint-disable-next-line no-console
Expand Down Expand Up @@ -413,6 +417,16 @@ export async function runExperiment(document, options, context) {
console.warn('Invalid experiment config. Please review your metadata, sheet and parser.');
return false;
}

const usp = new URLSearchParams(window.location.search);
const forcedVariant = usp.has(pluginOptions.experimentsQueryParameter)
? usp.get(pluginOptions.experimentsQueryParameter).split('/')[1]
: null;
if (!experimentConfig.run && !forcedVariant) {
// eslint-disable-next-line no-console
console.warn('Experiment will not run. It is either not active or its configured audiences are not resolved.');
return false;
}
// eslint-disable-next-line no-console
console.debug(`running experiment (${window.hlx.experiment.id}) -> ${window.hlx.experiment.selectedVariant}`);

Expand All @@ -434,7 +448,8 @@ export async function runExperiment(document, options, context) {

// Fullpage content experiment
document.body.classList.add(`experiment-${experimentConfig.id}`);
const result = await replaceInner(pages[0], document.querySelector('main'));
const result = await replaceInner(pages[index], document.querySelector('main'));
experimentConfig.servedExperience = result || currentPath;
if (!result) {
// eslint-disable-next-line no-console
console.debug(`failed to serve variant ${window.hlx.experiment.selectedVariant}. Falling back to ${experimentConfig.variantNames[0]}.`);
Expand Down Expand Up @@ -481,9 +496,12 @@ export async function runCampaign(document, options, context) {
return false;
}

window.hlx.campaign = { selectedCampaign: campaign };

try {
const url = new URL(urlString);
const result = replaceInner(url.pathname, document.querySelector('main'));
window.hlx.campaign.servedExperience = result || window.location.pathname;
if (!result) {
// eslint-disable-next-line no-console
console.debug(`failed to serve campaign ${campaign}. Falling back to default content.`);
Expand Down Expand Up @@ -513,7 +531,7 @@ export async function serveAudience(document, options, context) {
}

const audiences = await getResolvedAudiences(
Object.keys(configuredAudiences),
Object.keys(configuredAudiences).map(context.toClassName),
pluginOptions,
context,
);
Expand All @@ -526,17 +544,21 @@ export async function serveAudience(document, options, context) {
? context.toClassName(usp.get(pluginOptions.audiencesQueryParameter))
: null;

const urlString = configuredAudiences[forcedAudience || audiences[0]];
const selectedAudience = forcedAudience || audiences[0];
const urlString = configuredAudiences[selectedAudience];
if (!urlString) {
return false;
}

window.hlx.audience = { selectedAudience };

try {
const url = new URL(urlString);
const result = replaceInner(url.pathname, document.querySelector('main'));
window.hlx.audience.servedExperience = result || window.location.pathname;
if (!result) {
// eslint-disable-next-line no-console
console.debug(`failed to serve audience ${forcedAudience || audiences[0]}. Falling back to default content.`);
console.debug(`failed to serve audience ${selectedAudience}. Falling back to default content.`);
}
document.body.classList.add(audiences.map((audience) => `audience-${audience}`));
context.sampleRUM('audiences', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,9 @@ async function decorateExperimentPill(overlay, options, context) {
},
config.variantNames.map((vname) => createVariant(experiment, vname, config, options)),
);
pill.classList.add(`is-${context.toClassName(config.status)}`);
if (config.run) {
pill.classList.add(`is-${context.toClassName(config.status)}`);
}
overlay.append(pill);

const performanceMetrics = await fetchRumData(experiment, options);
Expand Down Expand Up @@ -424,7 +426,7 @@ async function decorateAudiencesPill(overlay, options, context) {
*/
export default async function decoratePreviewMode(document, options, context) {
try {
context.loadCSS(`${options.basePath || window.hlx.codeBasePath}/plugins/experience-decisioning/src/preview.css`);
context.loadCSS(`${options.basePath || window.hlx.codeBasePath}/plugins/experimentation/src/preview.css`);
const overlay = getOverlay(options);
await decorateAudiencesPill(overlay, options, context);
await decorateCampaignPill(overlay, options, context);
Expand Down
4 changes: 2 additions & 2 deletions scripts/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ window.hlx.plugins.add('rum-conversion', {
load: 'lazy',
});

window.hlx.plugins.add('experience-decisioning', {
window.hlx.plugins.add('experimentation', {
condition: () => getMetadata('experiment')
|| Object.keys(getAllMetadata('campaign')).length
|| Object.keys(getAllMetadata('audience')).length,
options: { audiences: AUDIENCES },
load: 'eager',
url: '/plugins/experience-decisioning/src/index.js',
url: '/plugins/experimentation/src/index.js',
});

/**
Expand Down
Loading