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

FEATURE: Automatic proposition interaction tracking #1101

Merged
merged 34 commits into from
Jun 5, 2024

Conversation

jasonwaters
Copy link
Collaborator

@jasonwaters jasonwaters commented Mar 4, 2024

Description

This PR is to support automatic proposition click tracking as described here.

In depth technical details about this feature can be read here

This change does not modify the way element clicks are tracked. Any element designated for click tracking (whether from AJO or Target) is tracked by iterating all known click selectors and checking if the element clicked and it's ancestors match. This functionality remains untouched.

This PR adds new functionality that tracks interactions (clicks) for proposition item(s) rendered to the DOM.
It does so using data-attributes. When alloy renders proposition items, any time the DOM is modified (either by inserting elements or mutating an existing DOM element) alloy adds a data attribute, data-aep-interact-id to the element, which is then used for interaction tracking. Interact ids have a one to many relationship with propositions and proposition-items. Since there can be a plethora of campaigns targeting the same element. For this reason, the interact id is used as a key for alloy to look up an in-memory mapping to see which propositions and items are relevant.

Additionally, a label and token may be added to an HTML proposition received from AJO using a data attribute.

Data attributes

HTML data attributes are used to store these details.

Name Data Attribute Source
Label data-aep-click-label Manually added to the html offer by customer during campaign creation.
Token data-aep-click-token Manually added to the html offer by customer during campaign creation.
Interact ID data-aep-interact-id A unique ID automatically added to container elements by alloy when rendering DOM actions. Each interact-id maps to at least one proposition, but it can map to more than one if multiple propositions reference the same element.

ℹ️ These 👆 names are tentative and subject to change. Please offer your feedback for naming if you have an opinion. ℹ️

Determining proposition, label & token when the user clicks within a proposition element

Beginning with the clicked element, each element's data attributes for click-label, click-token and interact-id are read. If a value is found on the element, it is used for the interact event. If not, the parent element is inspected for the data attributes. If not found there, each parent element thereafter will be inspected until the root element has been reached. Note that there could exist nested labels and tokens within elements. In that case, only the first label and token values found while walking up the DOM tree will be used.

New config option

A new optional configuration option has been added called autoCollectPropositionInteractions. The value is a map of decision providers, each with value that indicates how automatic proposition interactions should be handled.

By default, automatic proposition interactions are always collected for AJO, and never collected for Target. The default value of autoCollectPropositionInteractions is:

{
  "AJO": "always",
  "TGT": "never"
}

Possible configuration values for a decision provider are:

Value Description of behavior
always Alloy will always automatically emit interact events for any elements associated with a proposition.
never Alloy will never automatically emit interact events for DOM elements associated with a proposition.
decoratedElementsOnly Alloy will automatically emit interact events for elements associated with a proposition but ONLY if the element has also have been "decorated" with data attributes specifying a label or token.

enhanced applyPropositions command.

The applyPropositions command now allows users to associate a json-content-item proposition item with a DOM element. This comes in handy for code based JSON content propositions.

For example, imagine a customer has a json-content-item they used to generate a dom element or widget. They want to take advantage of automatic click tracking for that element. So they use the applyPropositions command to associate the proposition with the element selector. Sample code...

alloy("sendEvent", {
  renderDecisions: true,
}).then((result) => {
  const { propositions = [] } = result;
  const proposition = propositions.find(
    (proposition) => proposition.scope === "web://aepdemo.com/#woof"
  );

  if (proposition) {
    renderMyPropositionToDomInSuperCoolWay(proposition);

    alloy("applyPropositions", {
      propositions: [proposition],
      metadata: {
        "web://aepdemo.com/#woof": {
          selector: "div.woof-widget",
          actionType: "collectInteractions",
        },
      },
    });
  }
});

Alloy adds an interact-id to the element, and associates it with the provided proposition. Now automatic click tracking will work the same way it does for other automatically rendered propositions.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Improvement (non-breaking change which does not add functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • I have signed the Adobe Open Source CLA or I'm an Adobe employee.
  • I have made any necessary test changes and all tests pass.
  • I have run the Sandbox successfully.

@jfkhoury
Copy link
Contributor

Thank you @jasonwaters. 2 questions:

  1. How does this change affect Target?
  2. Does this feature require any changes to our Tags extension?

@jasonwaters
Copy link
Collaborator Author

jasonwaters commented Mar 14, 2024

@jfkhoury

  1. Automatic click tracking is enabled only for AJO, disabled for TGT. But, customers can enable it for target using the autoCollectPropositionInteractions configuration option.
  2. We need to add the config option for autoCollectPropositionInteractions to the tags extension config UI.

Copy link
Contributor

@jonsnyder jonsnyder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Of course we'll need functional tests. I'm wondering what the use case is for changing the "always", "never" fields. It will be good to review all the configuration parameters.

@jonsnyder jonsnyder changed the title Automatic click tracking Added support for AJO automatic click tracking May 8, 2024
# Conflicts:
#	src/components/Personalization/index.js
# Conflicts:
#	package-lock.json
#	package.json
#	src/components/Personalization/createApplyPropositions.js
#	src/components/Personalization/createClickStorage.js
#	src/components/Personalization/createOnClickHandler.js
#	src/components/Personalization/dom-actions/action.js
#	src/components/Personalization/dom-actions/appendHtml.js
#	src/components/Personalization/dom-actions/clicks/collectClicks.js
#	src/components/Personalization/dom-actions/initDomActionsModules.js
#	src/components/Personalization/dom-actions/insertHtmlAfter.js
#	src/components/Personalization/dom-actions/insertHtmlBefore.js
#	src/components/Personalization/dom-actions/prependHtml.js
#	src/components/Personalization/dom-actions/remapCustomCodeOffers.js
#	src/components/Personalization/dom-actions/remapHeadOffers.js
#	src/components/Personalization/dom-actions/setAttributes.js
#	src/components/Personalization/dom-actions/setStyles.js
#	src/components/Personalization/dom-actions/swapImage.js
#	src/components/Personalization/handlers/createProcessDomAction.js
#	src/components/Personalization/handlers/createProcessHtmlContent.js
#	src/components/Personalization/handlers/injectCreateProposition.js
#	src/components/Personalization/index.js
#	src/utils/dom/removeNode.js
#	test/functional/helpers/assertions/responseStatus.js
#	test/functional/specs/Config Overrides/C7437533.js
#	test/functional/specs/LibraryInfo/C2589.js
#	test/functional/specs/Migration/C8085775.js
#	test/functional/specs/Migration/C8085776.js
#	test/functional/specs/Migration/C8085779.js
#	test/functional/specs/Migration/C8085780.js
#	test/functional/specs/Privacy/C28754.js
#	test/unit/specs/components/Personalization/createApplyPropositions.spec.js
#	test/unit/specs/components/Personalization/createClickStorage.spec.js
#	test/unit/specs/components/Personalization/createComponent.spec.js
#	test/unit/specs/components/Personalization/createNotificationHandler.spec.js
#	test/unit/specs/components/Personalization/createOnClickHandler.spec.js
#	test/unit/specs/components/Personalization/dom-actions/appendHtml.spec.js
#	test/unit/specs/components/Personalization/dom-actions/clicks/collectClicks.spec.js
#	test/unit/specs/components/Personalization/dom-actions/createRedirect.spec.js
#	test/unit/specs/components/Personalization/dom-actions/customCode.spec.js
#	test/unit/specs/components/Personalization/dom-actions/initDomActionsModules.spec.js
#	test/unit/specs/components/Personalization/dom-actions/insertHtmlAfter.spec.js
#	test/unit/specs/components/Personalization/dom-actions/insertHtmlBefore.spec.js
#	test/unit/specs/components/Personalization/dom-actions/move.spec.js
#	test/unit/specs/components/Personalization/dom-actions/prependHtml.spec.js
#	test/unit/specs/components/Personalization/dom-actions/rearrangeChildren.spec.js
#	test/unit/specs/components/Personalization/dom-actions/remove.spec.js
#	test/unit/specs/components/Personalization/dom-actions/replaceHtml.spec.js
#	test/unit/specs/components/Personalization/dom-actions/resize.spec.js
#	test/unit/specs/components/Personalization/dom-actions/setAttributes.spec.js
#	test/unit/specs/components/Personalization/dom-actions/setHtml.spec.js
#	test/unit/specs/components/Personalization/dom-actions/setImageSource.spec.js
#	test/unit/specs/components/Personalization/dom-actions/setStyles.spec.js
#	test/unit/specs/components/Personalization/dom-actions/setText.spec.js
#	test/unit/specs/components/Personalization/handlers/createProcessDomAction.spec.js
#	test/unit/specs/components/Personalization/handlers/createProcessHtmlContent.spec.js
#	test/unit/specs/components/Personalization/handlers/createProcessPropositions.spec.js
#	test/unit/specs/components/Personalization/topLevel/buildAlloy.js
#	test/unit/specs/components/Personalization/topLevel/buildMocks.js
#	test/unit/specs/components/Personalization/topLevel/mixedPropositions.spec.js
#	test/unit/specs/components/Personalization/topLevel/pageWideDecisionsWithDomActionSchemaItems.spec.js
#	test/unit/specs/components/Personalization/topLevel/pageWideScopeDecisions.spec.js
#	test/unit/specs/components/Personalization/validateApplyPropositionsOptions.spec.js
#	test/unit/specs/utils/assignConcatArrayValues.spec.js
#	test/unit/specs/utils/createSubscription.spec.js
Copy link
Contributor

@ninaceban ninaceban left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Jason, the PR looks good!

The feature for automatically collecting clicks on personalized content, as per Erik's Wiki, is intended solely for HTML rendered content (either VEC or html-content-item). This feature is only supported by AJO, with Target only providing support for explicit click-tracking.

Upon reviewing the wiki, I found no requirement for automatic click tracking on json-content-item offers, leading to the introduction of a new action type, collectInteractions. Unless there's a specific customer request or use-case, I would advise against adding a new capability to Alloy.

src/utils/dom/removeNode.js Outdated Show resolved Hide resolved
src/utils/dom/awaitSelector.js Show resolved Hide resolved
// .expect(
// notification.events[0].xdm._experience.decisioning.propositions[0].id,
// )
// .eql(propositionId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we still need these commented lines?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest keeping them and un-commenting them when the TODO: Testcafe no longer captures the request body for sendBeacon requests. is resolved.

After merging main to my branch, I noticed testcafe was upgraded to the next major version. This upgrade broke the test. Version 3 of test cafe correctly captured request body for sendBeacon requests. I'm not sure if this is a regression on their part or an intentional change. But it definitely made it so we can't test as deeply as before.

// await t.expect(notification.events[0].xdm.eventType).eql(INTERACT);
//
// await t
// .expect(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should check the request body for features like click-tracking. If Testcafe doesn't capture the request body for sendBeacon request then for testing purposes we should force interact or find a way to validate we send the right payload.

cc: @jonsnyder @jfkhoury

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please let me know if there is a way to force fetch instead of sendBeacon for collect calls. I tried to find a way but didn't see a clear path.

@jasonwaters
Copy link
Collaborator Author

Hi Jason, the PR looks good!

The feature for automatically collecting clicks on personalized content, as per Erik's Wiki, is intended solely for HTML rendered content (either VEC or html-content-item). This feature is only supported by AJO, with Target only providing support for explicit click-tracking.

Upon reviewing the wiki, I found no requirement for automatic click tracking on json-content-item offers, leading to the introduction of a new action type, collectInteractions. Unless there's a specific customer request or use-case, I would advise against adding a new capability to Alloy.

Thanks @ninaceban ! You're right, Erik's wiki doesn't mention tracking json-content-item experiences. It was a case he brought up with me during development. If a customer uses applyPropositions with an html-content-item, an interact-id will be applied to that DOM element automatically for interaction tracking. But the same was not true for json-content-item. So the question was: How can customers benefit from automatic proposition interaction tracking in a simple way when they render something to the DOM based on a json-content-item code-based experience? To support that case, I added a new command: trackProposition. However, @jonsnyder made an excellent recommendation to remove that new command and instead enhance applyPropositions to suport json-content-item with a new actionType of collectInteractions. I made that change, and now customers can also easily track DOM elements they manually render based on a json-content-item. What are the down sides and risks for keeping this functionality?

BTW, you're right about target services not fully benefiting from automatic interaction tracking yet. I'm not sure the product roadmap priorities for target, but I believe it is on the backlog.

@jasonwaters jasonwaters changed the title Added support for AJO automatic click tracking FEATURE: Automatic proposition interaction tracking May 24, 2024
@ninaceban
Copy link
Contributor

BTW, you're right about target services not fully benefiting from automatic interaction tracking yet. I'm not sure the product roadmap priorities for target, but I believe it is on the backlog.

Given the uncertainty surrounding Target's plans to adopt the automatic click tracking feature, it would be advisable to avoid adding it now. The same applies to json-content-item items.

It's always easier to add features later than to remove them.

setAttribute(container, key, attributes[key]);
});
return Promise.resolve();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was this left after sequential rendering revert? There are few other dom-actions files that return a Promise.resolve() too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ninaceban i'll remove them if you want, but these actions should all return promises to be consistent with actions that require it such as appendHtml, redirect, insertHtmlBefore, insertHtmlAfer, prependHtml, replaceHtml and setHtml. Is there a good reason to have inconsistent returns across all the dom-actions? Or should there be a strong contract/pattern for each dom action to follow?

@jasonwaters
Copy link
Collaborator Author

BTW, you're right about target services not fully benefiting from automatic interaction tracking yet. I'm not sure the product roadmap priorities for target, but I believe it is on the backlog.

Given the uncertainty surrounding Target's plans to adopt the automatic click tracking feature, it would be advisable to avoid adding it now. The same applies to json-content-item items.

It's always easier to add features later than to remove them.

@ninaceban I'm not sure if there is an action you want me to take here. can you please be specific? It would be advisable to avoid adding what? If you are asking to not add automatic proposition interaction tracking for target then no worries -- that's already done. It is disabled for target by default.

As for the json-content-item tracking via applyPropositions, could you please raise your concern with Erik? He explicitly asked for this gap to be covered.

@jonsnyder jonsnyder merged commit 3ec5f6d into main Jun 5, 2024
4 checks passed
@jonsnyder jonsnyder deleted the auto-click-tracking branch June 5, 2024 15:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants