├── .editorconfig
├── .github
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── advanced-atjs-integration-serverstate
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── AppMeasurement.js
│ ├── VisitorAPI.js
│ └── at.js
├── server.js
└── templates
│ └── index.tpl
├── ecid-analytics-atjs-integration
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── AppMeasurement.js
│ ├── VisitorAPI.js
│ └── at.js
├── server.js
└── templates
│ └── index.tpl
├── ecid-analytics-integration
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── AppMeasurement.js
│ └── VisitorAPI.js
├── server.js
└── templates
│ └── index.tpl
├── ecid-customer-ids-integration
├── README.md
├── package-lock.json
├── package.json
├── public
│ └── VisitorAPI.js
├── server.js
└── templates
│ └── index.tpl
├── ecid-integration
├── README.md
├── package-lock.json
├── package.json
├── public
│ └── VisitorAPI.js
├── server.js
└── templates
│ └── index.tpl
├── feature-flag
├── README.md
├── index.handlebars
├── package-lock.json
├── package.json
├── provider.js
├── public
│ ├── demo-marketing-offer1-exp-A.png
│ ├── demo-marketing-offer1-exp-B.png
│ └── style.css
├── sampleRules.json
└── server.js
├── multiple-mbox-ecid-analytics-atjs-integration
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── AppMeasurement.js
│ ├── VisitorAPI.js
│ └── at.js
├── server.js
└── templates
│ └── index.tpl
├── next-server-side-rendering-demo
├── .gitignore
├── README.md
├── components
│ ├── Header.js
│ └── MyLayout.js
├── helpers
│ ├── target-client-side.js
│ ├── target-config.json
│ └── target-server-side.js
├── package-lock.json
├── package.json
├── pages
│ ├── _document.js
│ ├── about.js
│ ├── index.js
│ └── p
│ │ └── [id].js
└── static
│ ├── AppMeasurement.js
│ ├── VisitorAPI.js
│ └── at.js
├── on-device-decisioning
├── README.md
├── index.handlebars
├── package-lock.json
├── package.json
├── public
│ ├── demo-marketing-offer1-exp-A.png
│ ├── demo-marketing-offer1-exp-B.png
│ └── style.css
├── sampleRules.json
└── server.js
├── proxy-configuration
├── README.md
├── example-proxy-server.js
├── package-lock.json
├── package.json
├── proxy-sample-node-14.js
├── proxy-sample-node-16.js
└── proxy-sample-node-18.js
├── react-shopping-cart-demo
├── .babelrc
├── .gitignore
├── README.md
├── config.json
├── index.tpl
├── package-lock.json
├── package.json
├── public
│ ├── AppMeasurement.js
│ ├── VisitorAPI.js
│ ├── assets
│ │ ├── css
│ │ │ ├── app.css
│ │ │ ├── app.css.map
│ │ │ └── base.min.css
│ │ ├── js
│ │ │ └── app.js
│ │ └── resources
│ │ │ ├── data
│ │ │ ├── latestProducts.json
│ │ │ └── products.json
│ │ │ └── images
│ │ │ ├── carousel
│ │ │ ├── bigsale.png
│ │ │ ├── discount.png
│ │ │ ├── easter.png
│ │ │ ├── family.png
│ │ │ ├── happy.png
│ │ │ └── percent.png
│ │ │ ├── logo.png
│ │ │ ├── poc.png
│ │ │ ├── products
│ │ │ ├── baby.png
│ │ │ ├── bag.jpeg
│ │ │ ├── floraldress.png
│ │ │ ├── headphone.png
│ │ │ ├── luggage.png
│ │ │ ├── moisturizer.png
│ │ │ ├── perfume.png
│ │ │ ├── shoes.png
│ │ │ ├── towels.png
│ │ │ ├── tshirt.png
│ │ │ └── watch.png
│ │ │ └── target200.png
│ └── at.js
├── server.js
├── src
│ ├── actions
│ │ ├── addToCart.js
│ │ ├── addToWishlist.js
│ │ ├── deleteCart.js
│ │ ├── fetchAbout.js
│ │ ├── fetchCart.js
│ │ ├── fetchLatestProducts.js
│ │ ├── fetchProduct.js
│ │ ├── fetchProducts.js
│ │ ├── fetchWishlist.js
│ │ ├── removeFromCart.js
│ │ └── removeFromWishlist.js
│ ├── components
│ │ ├── CartItem.jsx
│ │ ├── Footer.jsx
│ │ ├── ProductItem.jsx
│ │ └── WishlistItem.jsx
│ ├── containers
│ │ ├── About.jsx
│ │ ├── App.jsx
│ │ ├── Cart.jsx
│ │ ├── Checkout.js
│ │ ├── Confirm.js
│ │ ├── Home.jsx
│ │ ├── Navbar.jsx
│ │ ├── Products.js
│ │ ├── SingleProduct.jsx
│ │ └── Wishlist.jsx
│ ├── index.js
│ ├── middlewares
│ │ ├── Auth.js
│ │ └── Loading.js
│ ├── reducers
│ │ ├── AboutReducer.js
│ │ ├── CartReducer.js
│ │ ├── LatestProductsReducer.js
│ │ ├── LoadingReducer.js
│ │ ├── ProductReducer.js
│ │ ├── ProductsReducer.js
│ │ ├── WishlistReducer.js
│ │ └── index.js
│ ├── store.js
│ ├── styles
│ │ └── sass
│ │ │ └── app.scss
│ └── utils
│ │ ├── customEvents.js
│ │ └── mockAxios.js
└── webpack.config.js
├── shared-ecid-analytics-integration
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── AppMeasurement.js
│ └── VisitorAPI.js
├── server.js
└── templates
│ └── index.tpl
├── target-only
├── README.md
├── package-lock.json
├── package.json
├── server.js
└── templates
│ └── index.tpl
└── target-traces
├── README.md
├── package-lock.json
├── package.json
├── server.js
└── templates
└── index.tpl
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 |
10 | [*.md]
11 | trim_trailing_whitespace = false
12 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thanks for choosing to contribute!
4 |
5 | The following are a set of guidelines to follow when contributing to this project.
6 |
7 | ## Code Of Conduct
8 |
9 | This project adheres to the Adobe [code of conduct](../CODE_OF_CONDUCT.md). By participating,
10 | you are expected to uphold this code. Please report unacceptable behavior to
11 | [Grp-opensourceoffice@adobe.com](mailto:Grp-opensourceoffice@adobe.com).
12 |
13 | ## Have A Question?
14 |
15 | Start by filing an issue. The existing committers on this project work to reach
16 | consensus around project direction and issue solutions within issue threads
17 | (when appropriate).
18 |
19 | ## Contributor License Agreement
20 |
21 | All third-party contributions to this project must be accompanied by a signed contributor
22 | license agreement. This gives Adobe permission to redistribute your contributions
23 | as part of the project. [Sign our CLA](https://opensource.adobe.com/cla.html). You
24 | only need to submit an Adobe CLA one time, so if you have submitted one previously,
25 | you are good to go!
26 |
27 | ## Code Reviews
28 |
29 | All submissions should come in the form of pull requests and need to be reviewed
30 | by project committers. Read [GitHub's pull request documentation](https://help.github.com/articles/about-pull-requests/)
31 | for more information on sending pull requests.
32 |
33 | Lastly, please follow the [pull request template](PULL_REQUEST_TEMPLATE.md) when
34 | submitting a pull request!
35 |
36 | ## From Contributor To Committer
37 |
38 | We love contributions from our community! If you'd like to go a step beyond contributor
39 | and become a committer with full write access and a say in the project, you must
40 | be invited to the project. The existing committers employ an internal nomination
41 | process that must reach lazy consensus (silence is approval) before invitations
42 | are issued. If you feel you are qualified and want to get more deeply involved,
43 | feel free to reach out to existing committers to have a conversation about that.
44 |
45 | ## Security Issues
46 |
47 | Security issues shouldn't be reported on this issue tracker. Instead, [file an issue to our security experts](https://helpx.adobe.com/security/alertus.html)
48 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ### Expected Behaviour
5 |
6 | ### Actual Behaviour
7 |
8 | ### Reproduce Scenario (including but not limited to)
9 |
10 | #### Steps to Reproduce
11 |
12 | #### Platform and Version
13 |
14 | #### Sample Code that illustrates the problem
15 |
16 | #### Logs taken while reproducing problem
17 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Description
4 |
5 |
6 |
7 | ## Related Issue
8 |
9 |
10 |
11 |
12 |
13 |
14 | ## Motivation and Context
15 |
16 |
17 |
18 | ## How Has This Been Tested?
19 |
20 |
21 |
22 |
23 |
24 | ## Screenshots (if appropriate):
25 |
26 | ## Types of changes
27 |
28 |
29 |
30 | - [ ] Bug fix (non-breaking change which fixes an issue)
31 | - [ ] New feature (non-breaking change which adds functionality)
32 | - [ ] Breaking change (fix or feature that would cause existing functionality to change)
33 |
34 | ## Checklist:
35 |
36 |
37 |
38 |
39 | - [ ] I have signed the [Adobe Open Source CLA](https://opensource.adobe.com/cla.html).
40 | - [ ] My code follows the code style of this project.
41 | - [ ] My change requires a change to the documentation.
42 | - [ ] I have updated the documentation accordingly.
43 | - [ ] I have read the **CONTRIBUTING** document.
44 | - [ ] I have added tests to cover my changes.
45 | - [ ] All new and existing tests passed.
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # package directories
2 | node_modules
3 | jspm_packages
4 | .idea
5 | .DS_Store
6 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Adobe Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at Grp-opensourceoffice@adobe.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [https://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: https://contributor-covenant.org
74 | [version]: https://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Target Node.js SDK Samples & Demos
2 |
3 | This repository contains samples and demos for the [Target Node.js SDK](https://www.npmjs.com/package/@adobe/target-nodejs-sdk)
4 |
5 | ## Demos
6 |
7 | - [react-shopping-cart-demo](react-shopping-cart-demo) - a demo showing how to fetch and inject Target offers in a
8 | [React Redux](https://react-redux.js.org/) app on the server side and then instantly apply the offers on the client side,
9 | without any additional client-side Target calls.
10 | - [next-server-side-rendering-demo](next-server-side-rendering-demo) - a demo showing how to fetch and inject Target offers
11 | in a [Next.js](https://nextjs.org/) server-side rendered app, and then instantly apply the offers on the client side,
12 | without any additional client-side Target calls.
13 |
14 | ## Samples
15 |
16 | - [target-only](target-only) - shows how Target Node.js SDK can be used in isolation
17 | - [ecid-integration](ecid-integration) - shows how Target Node.js SDK can be used in conjunction with ECID (Visitor API)
18 | - [ecid-customer-ids-integration](ecid-customer-ids-integration) - shows how Target Node.js SDK can be used in
19 | conjunction with ECID (Visitor API) and Customer IDs, useful for tracking user authentication.
20 | - [ecid-analytics-integration](ecid-analytics-integration) - shows how Target Node.js SDK can be used in conjunction
21 | with ECID (Visitor API) and Adobe Analytics.
22 | - [ecid-analytics-atjs-integration](ecid-analytics-atjs-integration) - shows how Target Node.js SDK can be used in
23 | conjunction with ECID (Visitor API), Adobe Analytics and at.js. This sample shows how to run testing in "hybrid" mode,
24 | when the test is started on the server side and then it is handed off to at.js on the client side.
25 | - [advanced-atjs-integration-serverstate](advanced-atjs-integration-serverstate) - shows how to use at.js v2.2+ **serverState** feature to apply Target offers fetched on the server side, without any additional client side content-fetching Target calls.
26 | - [target-traces](target-traces) - shows how Target Node.js SDK can be used to retrieve Target execution traces.
27 | - [shared-ecid-analytics-integration](shared-ecid-analytics-integration) - shows how to properly handle multiple Target
28 | Node.js SDK API calls when processing a client request, sharing the same ECID instance.
29 | - [multiple-mbox-ecid-analytics-atjs-integration](multiple-mbox-ecid-analytics-atjs-integration) - shows how Target
30 | Node.js SDK can be used to request content for multiple mboxes in the same Target call.
31 | - [on-device-decisioning](on-device-decisioning) - shows how Target Node.js SDK can be used in on-device decisioning method
32 | - [feature-flag](feature-flag) - shows how Target Node.js SDK can be easily used for feature flags
33 |
34 | For Target Node.js SDK documentation, see [Target Node.js SDK NPM page](https://www.npmjs.com/package/@adobe/target-nodejs-sdk).
35 |
36 | ## Contributing
37 |
38 | Check out our [Contribution guidelines](.github/CONTRIBUTING.md) as well as [Code of Conduct](CODE_OF_CONDUCT.md) prior
39 | to contributing to Target Node.js SDK samples.
40 |
--------------------------------------------------------------------------------
/advanced-atjs-integration-serverstate/README.md:
--------------------------------------------------------------------------------
1 | # Advanced at.js integration via serverState sample
2 |
3 | In the `ecid-analytics-atjs-integration` sample, we've showcased "hybrid" Target integration, where both the server-side and the client-side Target libraries are hitting the same Target edge cluster, sharing the same Target session and visitor state. However, at.js still needs to go over the wire for fetching Target content in the browser, prehiding the whole page BODY until Target offers are fetched and applied.
4 |
5 | But what if we could prefetch the Target content on the server-side, include it in the page returned to the client, and then just have at.js apply the Target offers immediately, without making another expensive network call?
6 | Also, in this case at.js will be able to prehide only the specific DOM elements for which Target offers have been fetched on the server-side, thus no longer requiring the prehiding of the whole page BODY.
7 |
8 | Target `serverState` is a new feature available in at.js v2.2+, that allows at.js to apply Target offers directly from content fetched on the server side and returned to the client as part of the page being served.
9 |
10 | In order to use this feature with Target Node.js SDK we just have to set `window.targetGlobalSettings.serverState` object in the returned page, from Target Delivery API request and response objects available after a successfull `getOffers()` API call, as follows:
11 |
12 | ```js
13 | // First, we fetch the offers via Target Node.js SDK API, as usual
14 | const targetResponse = await targetClient.getOffers(options);
15 | // A successfull response will contain Target Delivery API request and response objects, which we need to set as serverState
16 | const serverState = {
17 | request: targetResponse.request,
18 | response: targetResponse.response
19 | };
20 | // Finally, we should set window.targetGlobalSettings.serverState in the returned page, by replacing it in a page template, for example
21 | const PAGE_TEMPLATE = `
22 |
23 |
24 |
25 | ...
26 |
32 |
33 |
34 | ...
35 |
36 | `;
37 | // Return PAGE_TEMPLATE to the client ...
38 | ```
39 |
40 | Once the page is loaded in the browser, at.js will apply all the Target offers from `serverState` immediately, without firing any network calls against the Target edge. Additionally, at.js will only prehide the DOM elements for which Target offers are available in the content fetched server-side, thus greatly improving page load performance and end-user experience.
41 |
42 | Note: In case of SPAs using [Target Views and triggerView() at.js API](https://docs.adobe.com/content/help/en/target/using/implement-target/client-side/functions-overview/adobe-target-triggerview-atjs-2.html), at.js will cache the content for all Views prefetched on the server-side and apply these as soon as each View is triggered via `triggerView()`, again without firing any additional content-fetching calls to Target.
43 |
44 | ## Usage
45 | 1. Install dependencies: `npm i`
46 | 2. Start: `npm start`
47 |
--------------------------------------------------------------------------------
/advanced-atjs-integration-serverstate/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "advanced-atjs-integration-serverstate",
3 | "version": "1.0.0",
4 | "description": "Adobe Target Node.js SDK, advanced-atjs-integration-serverstate sample",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node server.js"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git@github.com:adobe/target-nodejs-sdk-samples.git"
12 | },
13 | "keywords": [
14 | "NodeJS",
15 | "Server",
16 | "API",
17 | "Adobe",
18 | "Target",
19 | "MCID",
20 | "ECID",
21 | "Visitor",
22 | "Delivery",
23 | "serverState",
24 | "advanced-atjs-integration-serverstate"
25 | ],
26 | "author": "Adobe Systems Inc.",
27 | "license": "Apache-2.0",
28 | "dependencies": {
29 | "@adobe/target-nodejs-sdk": "^2.1.0",
30 | "cookie-parser": "^1.4.4",
31 | "express": "^4.17.1"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/advanced-atjs-integration-serverstate/server.js:
--------------------------------------------------------------------------------
1 | /***************************************************************************************
2 | * (c) 2019 Adobe. All rights reserved.
3 | * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License. You may obtain a copy
5 | * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software distributed under
8 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 | * OF ANY KIND, either express or implied. See the License for the specific language
10 | * governing permissions and limitations under the License.
11 | ****************************************************************************************/
12 |
13 | const fs = require("fs");
14 | const express = require("express");
15 | const cookieParser = require("cookie-parser");
16 | const TargetClient = require("@adobe/target-nodejs-sdk");
17 | const CONFIG = {
18 | client: "adobetargetmobile",
19 | organizationId: "B8A054D958807F770A495DD6@AdobeOrg",
20 | timeout: 10000,
21 | logger: console
22 | };
23 | const targetClient = TargetClient.create(CONFIG);
24 | const TEMPLATE = fs.readFileSync(__dirname + "/templates/index.tpl").toString();
25 |
26 | const app = express();
27 | app.use(cookieParser());
28 | app.use(express.static(__dirname + "/public"));
29 |
30 | function saveCookie(res, cookie) {
31 | if (!cookie) {
32 | return;
33 | }
34 |
35 | res.cookie(cookie.name, cookie.value, { maxAge: cookie.maxAge * 1000 });
36 | }
37 |
38 | const getResponseHeaders = () => ({
39 | "Content-Type": "text/html",
40 | Expires: new Date().toUTCString()
41 | });
42 |
43 | function sendHtml(res, targetResponse) {
44 | const serverState = {
45 | request: targetResponse.request,
46 | response: targetResponse.response
47 | };
48 | const htmlResponse = TEMPLATE.replace(
49 | "${organizationId}",
50 | CONFIG.organizationId
51 | )
52 | .replace("${visitorState}", JSON.stringify(targetResponse.visitorState))
53 | .replace("${serverState}", JSON.stringify(serverState, null, " "))
54 | .replace("${content}", JSON.stringify(targetResponse, null, " "));
55 |
56 | res.status(200).send(htmlResponse);
57 | }
58 |
59 | function sendSuccessResponse(res, response) {
60 | res.set(getResponseHeaders());
61 | saveCookie(res, response.targetCookie);
62 | saveCookie(res, response.targetLocationHintCookie);
63 | sendHtml(res, response);
64 | }
65 |
66 | function sendErrorResponse(res, error) {
67 | res.set(getResponseHeaders());
68 | res.status(500).send(error);
69 | }
70 |
71 | function getAddress(req) {
72 | return { url: req.headers.host + req.originalUrl };
73 | }
74 |
75 | app.get("/", async (req, res) => {
76 | const visitorCookie =
77 | req.cookies[
78 | encodeURIComponent(
79 | TargetClient.getVisitorCookieName(CONFIG.organizationId)
80 | )
81 | ];
82 | const targetCookie = req.cookies[TargetClient.TargetCookieName];
83 | const targetLocationHintCookie =
84 | req.cookies[TargetClient.TargetLocationHintCookieName];
85 | const request = {
86 | execute: {
87 | mboxes: [
88 | {
89 | address: getAddress(req),
90 | name: "a1-serverside-ab"
91 | }
92 | ]
93 | }
94 | };
95 |
96 | try {
97 | const response = await targetClient.getOffers({
98 | request,
99 | visitorCookie,
100 | targetCookie,
101 | targetLocationHintCookie
102 | });
103 | sendSuccessResponse(res, response);
104 | } catch (error) {
105 | console.error("Target:", error);
106 | sendErrorResponse(res, error);
107 | }
108 | });
109 |
110 | app.listen(3000, function() {
111 | console.log("Listening on port 3000 and watching!");
112 | });
113 |
--------------------------------------------------------------------------------
/advanced-atjs-integration-serverstate/templates/index.tpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Advanced at.js Integration with ServerState Sample
6 |
7 |
10 |
16 |
17 |
18 |
19 |
${content}
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/ecid-analytics-atjs-integration/README.md:
--------------------------------------------------------------------------------
1 | # ECID, Analytics and at.js integration sample
2 |
3 | Most of the time Target Node.js SDK will be used in a NodeJS application, such as `Express`, `Hapi`, `Koa`, etc.
4 | However, with the recent proliferation of SPA frameworks that allow server-side rendering (such as Facebook React,
5 | Next.js or Angular), there are use cases where server-side code should work in tandem with client-side libraries.
6 | In Target's case the client-side library is `at.js`.
7 | The integration between server-side and client-side is also known as "hybrid" testing mode.
8 | The biggest challenge in this case is ensuring that both server-side and client-side Target calls are hitting the same
9 | Target edge cluster. Otherwise, one may end up with different user profiles being created by server-side and client-side
10 | calls for the same visitor.
11 |
12 | To solve this, Target leverages the so-called "location hint" cookie. To be able to use the location hint cookie, the
13 | following JavaScript snippet must be added to the rendered page before `at.js` (or before the Target Adobe Launch extension
14 | is initialized when Adobe Launch tag manager is used):
15 |
16 | ```js
17 | window.targetGlobalSettings = {
18 | overrideMboxEdgeServer: true
19 | };
20 | ```
21 |
22 | ## Usage
23 | 1. Install dependencies: `npm i`
24 | 2. Start: `npm start`
25 |
--------------------------------------------------------------------------------
/ecid-analytics-atjs-integration/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ecid-analytics-atjs-integration",
3 | "version": "1.0.0",
4 | "description": "Adobe Target Node.js SDK, ecid-analytics-atjs-integration sample",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node server.js"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git@github.com:adobe/target-nodejs-sdk-samples.git"
12 | },
13 | "keywords": [
14 | "NodeJS",
15 | "Server",
16 | "API",
17 | "Adobe",
18 | "Target",
19 | "MCID",
20 | "ECID",
21 | "Visitor",
22 | "Delivery",
23 | "ecid-analytics-atjs-integration"
24 | ],
25 | "author": "Adobe Systems Inc.",
26 | "license": "Apache-2.0",
27 | "dependencies": {
28 | "@adobe/target-nodejs-sdk": "^2.1.0",
29 | "cookie-parser": "^1.4.4",
30 | "express": "^4.17.1"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ecid-analytics-atjs-integration/server.js:
--------------------------------------------------------------------------------
1 | /***************************************************************************************
2 | * (c) 2019 Adobe. All rights reserved.
3 | * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License. You may obtain a copy
5 | * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software distributed under
8 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 | * OF ANY KIND, either express or implied. See the License for the specific language
10 | * governing permissions and limitations under the License.
11 | ****************************************************************************************/
12 |
13 | const fs = require("fs");
14 | const express = require("express");
15 | const cookieParser = require("cookie-parser");
16 | const TargetClient = require("@adobe/target-nodejs-sdk");
17 | const CONFIG = {
18 | client: "adobetargetmobile",
19 | organizationId: "B8A054D958807F770A495DD6@AdobeOrg",
20 | timeout: 10000,
21 | logger: console
22 | };
23 | const targetClient = TargetClient.create(CONFIG);
24 | const TEMPLATE = fs.readFileSync(__dirname + "/templates/index.tpl").toString();
25 |
26 | const app = express();
27 | app.use(cookieParser());
28 | app.use(express.static(__dirname + "/public"));
29 |
30 | function saveCookie(res, cookie) {
31 | if (!cookie) {
32 | return;
33 | }
34 |
35 | res.cookie(cookie.name, cookie.value, { maxAge: cookie.maxAge * 1000 });
36 | }
37 |
38 | const getResponseHeaders = () => ({
39 | "Content-Type": "text/html",
40 | Expires: new Date().toUTCString()
41 | });
42 |
43 | function sendHtml(res, offer) {
44 | const htmlResponse = TEMPLATE.replace(
45 | "${organizationId}",
46 | CONFIG.organizationId
47 | )
48 | .replace("${visitorState}", JSON.stringify(offer.visitorState))
49 | .replace("${content}", JSON.stringify(offer, null, " "));
50 |
51 | res.status(200).send(htmlResponse);
52 | }
53 |
54 | function sendSuccessResponse(res, response) {
55 | res.set(getResponseHeaders());
56 | saveCookie(res, response.targetCookie);
57 | saveCookie(res, response.targetLocationHintCookie);
58 | sendHtml(res, response);
59 | }
60 |
61 | function sendErrorResponse(res, error) {
62 | res.set(getResponseHeaders());
63 | res.status(500).send(error);
64 | }
65 |
66 | function getAddress(req) {
67 | return { url: req.headers.host + req.originalUrl };
68 | }
69 |
70 | app.get("/", async (req, res) => {
71 | const visitorCookie =
72 | req.cookies[
73 | encodeURIComponent(
74 | TargetClient.getVisitorCookieName(CONFIG.organizationId)
75 | )
76 | ];
77 | const targetCookie = req.cookies[TargetClient.TargetCookieName];
78 | const targetLocationHintCookie =
79 | req.cookies[TargetClient.TargetLocationHintCookieName];
80 | const request = {
81 | execute: {
82 | mboxes: [
83 | {
84 | address: getAddress(req),
85 | name: "a1-serverside-ab"
86 | }
87 | ]
88 | }
89 | };
90 |
91 | try {
92 | const response = await targetClient.getOffers({
93 | request,
94 | visitorCookie,
95 | targetCookie,
96 | targetLocationHintCookie
97 | });
98 | sendSuccessResponse(res, response);
99 | } catch (error) {
100 | console.error("Target:", error);
101 | sendErrorResponse(res, error);
102 | }
103 | });
104 |
105 | app.listen(3000, function() {
106 | console.log("Listening on port 3000 and watching!");
107 | });
108 |
--------------------------------------------------------------------------------
/ecid-analytics-atjs-integration/templates/index.tpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ECID (Visitor API) with Analytics and at.js Integration Sample
6 |
7 |
10 |
15 |
16 |
17 |
18 |
${content}
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ecid-analytics-integration/README.md:
--------------------------------------------------------------------------------
1 | # ECID and Analytics integration sample
2 |
3 | To get the most out of the Target Node.js SDK and to use the powerful analytics capabilities provided by Adobe Analytics,
4 | you can use the Target, ECID and Analytics combo.
5 |
6 | Using MCID, Analytics, and Target lets you:
7 | - Use segments from Adobe Audience Manager
8 | - Customize the user experience based on the content retrieved from Target
9 | - Ensure that all events and success metrics are collected in Analytics
10 | - Use Analytics' powerful queries and benefit from awesome report visualizations
11 |
12 | ## Usage
13 | 1. Install dependencies: `npm i`
14 | 2. Start: `npm start`
15 |
--------------------------------------------------------------------------------
/ecid-analytics-integration/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ecid-analytics-integration",
3 | "version": "1.0.0",
4 | "description": "Adobe Target Node.js SDK, ecid-analytics-integration sample",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node server.js"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git@github.com:adobe/target-nodejs-sdk-samples.git"
12 | },
13 | "keywords": [
14 | "NodeJS",
15 | "Server",
16 | "API",
17 | "Adobe",
18 | "Target",
19 | "MCID",
20 | "ECID",
21 | "Visitor",
22 | "Delivery",
23 | "ecid-analytics-integration"
24 | ],
25 | "author": "Adobe Systems Inc.",
26 | "license": "Apache-2.0",
27 | "dependencies": {
28 | "@adobe/target-nodejs-sdk": "^2.1.0",
29 | "cookie-parser": "^1.4.4",
30 | "express": "^4.17.1"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ecid-analytics-integration/server.js:
--------------------------------------------------------------------------------
1 | /***************************************************************************************
2 | * (c) 2019 Adobe. All rights reserved.
3 | * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License. You may obtain a copy
5 | * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software distributed under
8 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 | * OF ANY KIND, either express or implied. See the License for the specific language
10 | * governing permissions and limitations under the License.
11 | ****************************************************************************************/
12 |
13 | const fs = require("fs");
14 | const express = require("express");
15 | const cookieParser = require("cookie-parser");
16 | const TargetClient = require("@adobe/target-nodejs-sdk");
17 | const CONFIG = {
18 | client: "adobetargetmobile",
19 | organizationId: "B8A054D958807F770A495DD6@AdobeOrg",
20 | timeout: 10000,
21 | logger: console
22 | };
23 | const targetClient = TargetClient.create(CONFIG);
24 | const TEMPLATE = fs.readFileSync(__dirname + "/templates/index.tpl").toString();
25 |
26 | const app = express();
27 | app.use(cookieParser());
28 | app.use(express.static(__dirname + "/public"));
29 |
30 | function saveCookie(res, cookie) {
31 | if (!cookie) {
32 | return;
33 | }
34 |
35 | res.cookie(cookie.name, cookie.value, { maxAge: cookie.maxAge * 1000 });
36 | }
37 |
38 | const getResponseHeaders = () => ({
39 | "Content-Type": "text/html",
40 | Expires: new Date().toUTCString()
41 | });
42 |
43 | function sendHtml(res, offer) {
44 | const htmlResponse = TEMPLATE.replace(
45 | "${organizationId}",
46 | CONFIG.organizationId
47 | )
48 | .replace("${visitorState}", JSON.stringify(offer.visitorState))
49 | .replace("${content}", JSON.stringify(offer, null, " "));
50 |
51 | res.status(200).send(htmlResponse);
52 | }
53 |
54 | function sendSuccessResponse(res, response) {
55 | res.set(getResponseHeaders());
56 | saveCookie(res, response.targetCookie);
57 | sendHtml(res, response);
58 | }
59 |
60 | function sendErrorResponse(res, error) {
61 | res.set(getResponseHeaders());
62 | res.status(500).send(error);
63 | }
64 |
65 | function getAddress(req) {
66 | return { url: req.headers.host + req.originalUrl };
67 | }
68 |
69 | app.get("/", async (req, res) => {
70 | const visitorCookie =
71 | req.cookies[
72 | encodeURIComponent(
73 | TargetClient.getVisitorCookieName(CONFIG.organizationId)
74 | )
75 | ];
76 | const targetCookie = req.cookies[TargetClient.TargetCookieName];
77 | const request = {
78 | execute: {
79 | mboxes: [
80 | {
81 | address: getAddress(req),
82 | name: "a1-serverside-ab"
83 | }
84 | ]
85 | }
86 | };
87 |
88 | try {
89 | const response = await targetClient.getOffers({
90 | request,
91 | visitorCookie,
92 | targetCookie
93 | });
94 | sendSuccessResponse(res, response);
95 | } catch (error) {
96 | console.error("Target:", error);
97 | sendErrorResponse(res, error);
98 | }
99 | });
100 |
101 | app.listen(3000, function() {
102 | console.log("Listening on port 3000 and watching!");
103 | });
104 |
--------------------------------------------------------------------------------
/ecid-analytics-integration/templates/index.tpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ECID (Visitor API) with Analytics Integration Sample
6 |
7 |
10 |
11 |
12 |
${content}
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/ecid-customer-ids-integration/README.md:
--------------------------------------------------------------------------------
1 | # ECID with Customer Ids integration sample
2 |
3 | In order to track visitor user accounts and logon status details, `customerIds` may be passed to Target.
4 | The `customerIds` object is similar to the ECID functionality described here: https://docs.adobe.com/content/help/en/id-service/using/reference/authenticated-state.html
5 |
6 | ## Usage
7 | 1. Install dependencies: `npm i`
8 | 2. Start: `npm start`
9 |
--------------------------------------------------------------------------------
/ecid-customer-ids-integration/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ecid-customer-ids-integration",
3 | "version": "1.0.0",
4 | "description": "Adobe Target Node.js SDK, ecid-customer-ids-integration sample",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node server.js"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git@github.com:adobe/target-nodejs-sdk-samples.git"
12 | },
13 | "keywords": [
14 | "NodeJS",
15 | "Server",
16 | "API",
17 | "Adobe",
18 | "Target",
19 | "MCID",
20 | "ECID",
21 | "Visitor",
22 | "Delivery",
23 | "ecid-customer-ids-integration"
24 | ],
25 | "author": "Adobe Systems Inc.",
26 | "license": "Apache-2.0",
27 | "dependencies": {
28 | "@adobe/target-nodejs-sdk": "^2.1.0",
29 | "cookie-parser": "^1.4.4",
30 | "express": "^4.17.1"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ecid-customer-ids-integration/server.js:
--------------------------------------------------------------------------------
1 | /***************************************************************************************
2 | * (c) 2019 Adobe. All rights reserved.
3 | * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License. You may obtain a copy
5 | * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software distributed under
8 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 | * OF ANY KIND, either express or implied. See the License for the specific language
10 | * governing permissions and limitations under the License.
11 | ****************************************************************************************/
12 |
13 | const fs = require("fs");
14 | const express = require("express");
15 | const cookieParser = require("cookie-parser");
16 | const TargetClient = require("@adobe/target-nodejs-sdk");
17 | const CONFIG = {
18 | client: "adobetargetmobile",
19 | organizationId: "B8A054D958807F770A495DD6@AdobeOrg",
20 | timeout: 10000,
21 | logger: console
22 | };
23 | const targetClient = TargetClient.create(CONFIG);
24 | const TEMPLATE = fs.readFileSync(__dirname + "/templates/index.tpl").toString();
25 |
26 | const app = express();
27 | app.use(cookieParser());
28 | app.use(express.static(__dirname + "/public"));
29 |
30 | function saveCookie(res, cookie) {
31 | if (!cookie) {
32 | return;
33 | }
34 |
35 | res.cookie(cookie.name, cookie.value, { maxAge: cookie.maxAge * 1000 });
36 | }
37 |
38 | const getResponseHeaders = () => ({
39 | "Content-Type": "text/html",
40 | Expires: new Date().toUTCString()
41 | });
42 |
43 | function sendHtml(res, offer) {
44 | const htmlResponse = TEMPLATE.replace(
45 | "${organizationId}",
46 | CONFIG.organizationId
47 | )
48 | .replace("${visitorState}", JSON.stringify(offer.visitorState))
49 | .replace("${content}", JSON.stringify(offer, null, " "));
50 |
51 | res.status(200).send(htmlResponse);
52 | }
53 |
54 | function sendSuccessResponse(res, response) {
55 | res.set(getResponseHeaders());
56 | saveCookie(res, response.targetCookie);
57 | sendHtml(res, response);
58 | }
59 |
60 | function sendErrorResponse(res, error) {
61 | res.set(getResponseHeaders());
62 | res.status(500).send(error);
63 | }
64 |
65 | function getAddress(req) {
66 | return { url: req.headers.host + req.originalUrl };
67 | }
68 |
69 | app.get("/", async (req, res) => {
70 | const visitorCookie =
71 | req.cookies[
72 | encodeURIComponent(
73 | TargetClient.getVisitorCookieName(CONFIG.organizationId)
74 | )
75 | ];
76 | const targetCookie = req.cookies[TargetClient.TargetCookieName];
77 | const customerIds = {
78 | userid: {
79 | id: "67312378756723456",
80 | authState: TargetClient.AuthState.AUTHENTICATED
81 | }
82 | };
83 | const request = {
84 | execute: {
85 | mboxes: [
86 | {
87 | address: getAddress(req),
88 | name: "a1-serverside-ab"
89 | }
90 | ]
91 | }
92 | };
93 |
94 | try {
95 | const response = await targetClient.getOffers({
96 | request,
97 | visitorCookie,
98 | targetCookie,
99 | customerIds
100 | });
101 | sendSuccessResponse(res, response);
102 | } catch (error) {
103 | console.error("Target:", error);
104 | sendErrorResponse(res, error);
105 | }
106 | });
107 |
108 | app.listen(3000, function() {
109 | console.log("Listening on port 3000 and watching!");
110 | });
111 |
--------------------------------------------------------------------------------
/ecid-customer-ids-integration/templates/index.tpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ECID (Visitor API) with Customer IDs Integration Sample
6 |
7 |
10 |
11 |
12 |
${content}
13 |
14 |
15 |
--------------------------------------------------------------------------------
/ecid-integration/README.md:
--------------------------------------------------------------------------------
1 | # ECID integration sample
2 |
3 | Although using the Target Node.js SDK for fetching content from Target can be powerful, the added value of using ECID
4 | for user tracking outweighs using Target only. ECID allows leveraging all the cool features of the Adobe Experience Cloud,
5 | such as audience sharing, analytics integration, etc.
6 | Using Target and ECID in an `Express` application is pretty straightforward. ECID has a client-side part, so we'll have
7 | to use a simple template that references the ECID client-side JavaScript library.
8 |
9 | ## Usage
10 | 1. Install dependencies: `npm i`
11 | 2. Start: `npm start`
12 |
--------------------------------------------------------------------------------
/ecid-integration/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ecid-integration",
3 | "version": "1.0.0",
4 | "description": "Adobe Target Node.js SDK, ecid-integration sample",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node server.js"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git@github.com:adobe/target-nodejs-sdk-samples.git"
12 | },
13 | "keywords": [
14 | "NodeJS",
15 | "Server",
16 | "API",
17 | "Adobe",
18 | "Target",
19 | "MCID",
20 | "ECID",
21 | "Visitor",
22 | "Delivery",
23 | "ecid-integration"
24 | ],
25 | "author": "Adobe Systems Inc.",
26 | "license": "Apache-2.0",
27 | "dependencies": {
28 | "@adobe/target-nodejs-sdk": "^2.1.0",
29 | "cookie-parser": "^1.4.4",
30 | "express": "^4.17.1"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ecid-integration/server.js:
--------------------------------------------------------------------------------
1 | /***************************************************************************************
2 | * (c) 2019 Adobe. All rights reserved.
3 | * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License. You may obtain a copy
5 | * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software distributed under
8 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 | * OF ANY KIND, either express or implied. See the License for the specific language
10 | * governing permissions and limitations under the License.
11 | ****************************************************************************************/
12 |
13 | const fs = require("fs");
14 | const express = require("express");
15 | const cookieParser = require("cookie-parser");
16 | const TargetClient = require("@adobe/target-nodejs-sdk");
17 | const CONFIG = {
18 | client: "adobetargetmobile",
19 | organizationId: "B8A054D958807F770A495DD6@AdobeOrg",
20 | timeout: 10000,
21 | logger: console
22 | };
23 | const targetClient = TargetClient.create(CONFIG);
24 | const TEMPLATE = fs.readFileSync(__dirname + "/templates/index.tpl").toString();
25 |
26 | const app = express();
27 | app.use(cookieParser());
28 | app.use(express.static(__dirname + "/public"));
29 |
30 | function saveCookie(res, cookie) {
31 | if (!cookie) {
32 | return;
33 | }
34 |
35 | res.cookie(cookie.name, cookie.value, { maxAge: cookie.maxAge * 1000 });
36 | }
37 |
38 | const getResponseHeaders = () => ({
39 | "Content-Type": "text/html",
40 | Expires: new Date().toUTCString()
41 | });
42 |
43 | function sendHtml(res, offer) {
44 | const htmlResponse = TEMPLATE.replace(
45 | "${organizationId}",
46 | CONFIG.organizationId
47 | )
48 | .replace("${visitorState}", JSON.stringify(offer.visitorState))
49 | .replace("${content}", JSON.stringify(offer, null, " "));
50 |
51 | res.status(200).send(htmlResponse);
52 | }
53 |
54 | function sendSuccessResponse(res, response) {
55 | res.set(getResponseHeaders());
56 | saveCookie(res, response.targetCookie);
57 | sendHtml(res, response);
58 | }
59 |
60 | function sendErrorResponse(res, error) {
61 | res.set(getResponseHeaders());
62 | res.status(500).send(error);
63 | }
64 |
65 | function getAddress(req) {
66 | return { url: req.headers.host + req.originalUrl };
67 | }
68 |
69 | app.get("/", async (req, res) => {
70 | const visitorCookie =
71 | req.cookies[
72 | encodeURIComponent(
73 | TargetClient.getVisitorCookieName(CONFIG.organizationId)
74 | )
75 | ];
76 | const targetCookie = req.cookies[TargetClient.TargetCookieName];
77 | const request = {
78 | execute: {
79 | mboxes: [
80 | {
81 | address: getAddress(req),
82 | name: "a1-serverside-ab"
83 | }
84 | ]
85 | }
86 | };
87 |
88 | try {
89 | const response = await targetClient.getOffers({
90 | request,
91 | visitorCookie,
92 | targetCookie
93 | });
94 | sendSuccessResponse(res, response);
95 | } catch (error) {
96 | console.error("Target:", error);
97 | sendErrorResponse(res, error);
98 | }
99 | });
100 |
101 | app.listen(3000, function() {
102 | console.log("Listening on port 3000 and watching!");
103 | });
104 |
--------------------------------------------------------------------------------
/ecid-integration/templates/index.tpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ECID (Visitor API) Integration Sample
6 |
7 |
10 |
11 |
12 |
${content}
13 |
14 |
15 |
--------------------------------------------------------------------------------
/feature-flag/README.md:
--------------------------------------------------------------------------------
1 | # Target feature flag sample
2 |
3 | ## Overview
4 |
5 | For this sample, we first created two simple AB activities. One to represent feature flags for engineering purposes and another for marketing purposes. Each activity has two experiences that have JSON offer content. The JSON holds unique key value pairs that are used by the sample app to determine what engineering systems to use and marketing content to show.
6 |
7 | ### Engineering Feature Flags Activity
8 | mbox: `demo-engineering-flags`
9 |
10 | #### Experience A
11 | ```json
12 | {
13 | "cdnHostname": "cdn.cloud.corp.net",
14 | "searchProviderId": "starwars",
15 | "hasLegacyAccess": false
16 | }
17 | ```
18 |
19 | #### Experience B
20 | ```json
21 | {
22 | "cdnHostname": "cdn.megacloud.corp.com",
23 | "searchProviderId":"startrek",
24 | "hasLegacyAccess": true
25 | }
26 | ```
27 |
28 | ### Marketing Activity
29 | mbox: `demo-marketing-offer1`
30 |
31 | ### Experience A
32 | ```json
33 | {
34 | "experience": "A",
35 | "asset": "demo-marketing-offer1-exp-A.png"
36 | }
37 | ```
38 | ### Experience B
39 |
40 | ```json
41 | {
42 | "experience": "B",
43 | "asset": "demo-marketing-offer1-exp-B.png"
44 | }
45 | ```
46 |
47 | When run, the sample app displays a marketing banner and a search box. The marketing banner is different depending on the `asset` value of the `demo-marketing-offer1` mbox. And the search experience differs depending on the `searchProviderId` value of the `demo-engineering-flags` mbox. If the value is `starwars`,a [Star Wars API](https://swapi.co/) is used to search for characters. If the value is `startrek`, a [Star Trek API](http://stapi.co/) is used to search for characters.
48 |
49 | In this sample, the `getAttributes` call is used to greatly simplify accessing the JSON offer. Typically, a developer would need to find the JSON offer object in the response of the `getOffers` call. This is done in other samples. It is straightforward, but can be cumbersome -- and it requires developers to be intimately familiar with the SDK response object.
50 |
51 | Instead, this sample uses the `getAttributes` call to get the offer instead. It then looks up the value of each attribute using one of the helper methods.
52 |
53 | ## Running the sample
54 | 1. Install dependencies: `npm i`
55 | 2. Start: `npm start`
56 | 3. Point a browser to http://127.0.0.1:3000
57 |
58 |
59 | ## How it works
60 |
61 | In the code sample below, take a look at the `getAttributes` call. An array of mbox names and an options object is passed in. The result is an attributes object with a few methods that can be used to get offer details.
62 |
63 | The `getValue` method is used to get the `searchProviderId` from the `demo-engineering-flags` mbox offer.
64 |
65 | And the `asObject` method is used to get a plain old JSON representation of the `demo-marketing-offer1` mbox offer.
66 |
67 | ```js
68 | const targetClient = TargetClient.create(CONFIG);
69 | const offerAttributes = await targetClient.getAttributes([
70 | "demo-engineering-flags",
71 | "demo-marketing-offer1",
72 | ], { targetCookie });
73 |
74 |
75 | //returns just the value of searchProviderId from the mbox offer
76 | const searchProviderId = offerAttributes.getValue("demo-engineering-flags", "searchProviderId");
77 |
78 | //returns a simple JSON object representing the mbox offer
79 | const marketingOffer = offerAttributes.asObject("demo-marketing-offer1");
80 |
81 | // the value of marketingOffer looks like this
82 | // {
83 | // "experience": "A",
84 | // "asset": "demo-marketing-offer1-exp-A.png"
85 | // }
86 |
87 | ```
88 |
89 | Note: This sample uses on-device decisioning method. But the `getAttributes` method can be used in any decisioning method.
90 |
--------------------------------------------------------------------------------
/feature-flag/index.handlebars:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{pageTitle}}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
17 |
18 |
19 | );
20 | };
21 |
22 | Post.getInitialProps = async function(context) {
23 | const { id } = context.query;
24 | const res = await fetch(`https://api.tvmaze.com/shows/${id}`);
25 | const show = await res.json();
26 |
27 | console.log(`Fetched show: ${show.name}`);
28 |
29 | return { show };
30 | };
31 |
32 | export default Post;
33 |
--------------------------------------------------------------------------------
/on-device-decisioning/README.md:
--------------------------------------------------------------------------------
1 | # Target on-device decisioning sample
2 |
3 | ## Overview
4 |
5 | For this sample, we first created a simple AB activity for the `demo-marketing-offer1` mbox. It has two experiences, each with JSON offer content.
6 |
7 | ### Experience A
8 | ```json
9 | {
10 | "experience": "A",
11 | "asset": "demo-marketing-offer1-exp-A.png"
12 | }
13 | ```
14 | ### Experience B
15 |
16 | ```json
17 | {
18 | "experience": "B",
19 | "asset": "demo-marketing-offer1-exp-B.png"
20 | }
21 | ```
22 |
23 | As you can see, each experience has a different filename set in the `asset` property.
24 |
25 | When run, the app server makes a getOffers call, requesting the `demo-marketing-offer1` mbox. But the SDK has been configured to use on-device decisioning method to determine the outcome of the call rather than send a request to the target delivery API.
26 |
27 | When the page is loaded in a browser, an image is shown at the top of the page. This image comes from one of the two experiences in the activity defined above. The target response is also shown on the page.
28 |
29 | ## Running the sample
30 | 1. Install dependencies: `npm i`
31 | 2. Start: `npm start`
32 | 3. Point a browser to http://127.0.0.1:3000
33 |
34 |
35 | ## How it works
36 |
37 | This sample utilizes on-device decisioning method to determine target experiences. By default, the SDK always makes a request to the target delivery API for each `getOffers` call. But you can configure the SDK to use on-device decisioning method instead. This mode downloads target activity rules on initialization. The rules are then used to determine which experiences to return when `getOffers` is called, rather than make a request to the delivery API each time.
38 |
39 | There are four main properties to keep in mind when using on-device decisioning method:
40 |
41 | | Name | Description |
42 | |---------------------------|-------------------------------------------------------------------------------------|
43 | | decisioningMethod | The decisioning method the SDK will run in. Can be `on-device`, `server-side`, or `hybrid`. Defaults to `server-side` |
44 | | artifactLocation | This is a fully qualified url to the rules definition file that will be used to determine outcomes locally. |
45 | | artifactPayload | A target decisioning JSON artifact. If specified, it is used instead of requesting one from a URL. |
46 | | events | Object. | No | None | An optional object with event name keys and callback function values. |
47 |
48 | NOTE: You must specify an `artifactLocation` or `artifactPayload` during the alpha release for on-device decisioning method to work.
49 |
50 | ```js
51 | const CONFIG = {
52 | decisioningMethod: "on-device",
53 | artifactPayload: require("sampleRules"),
54 | events: { clientReady: targetReady }
55 | };
56 |
57 | const targetClient = TargetClient.create(CONFIG);
58 |
59 | function targetReady() {
60 | // make getOffers requests
61 | // targetClient.getOffers({...})
62 | }
63 | ```
64 |
65 | Once configured in this way, and after the `clientReady` event callback has been invoked, an app can make standard SDK method calls as normal.
66 |
--------------------------------------------------------------------------------
/on-device-decisioning/index.handlebars:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{pageTitle}}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
Single page applications (SPAs) implemented on popular frameworks like React and Angular and custom
24 | implementation frameworks are the new norm for the websites of today's "modern web". We've been developing
25 | for SPAs and the modern web for a while now. Now, we are taking this ground-breaking research and applying
26 | it to the entirely new Visual Experience Composer for SPAs, giving marketers the agility and control they
27 | need to build rich, personalized experiences at scale, no matter what framework or architecture they use.
28 | Developers can do a one-time setup by including a single line of javascript code to enable their SPA
29 | websites for VEC. This first-of-a-kind product innovation enables non-technical marketers to experiment
30 | and personalize on popular SPA frameworks.
31 |
32 |
33 |
This website is built using React-Redux, one of the most popular frameworks these days. In this demo, we
34 | will see how we can create tests and personalize content on SPAs in a do-it-yourself fashion without
35 | continuous development dependencies.
36 |
37 |
38 |
To understand the methodology used in this site, please visit our Documentation page here.
41 |
42 |
43 |
44 |
45 | ReactJS and Redux are not Adobe proprietary technologies.
46 | Transactions on this site are not real.
47 | Forked from GitHub
48 |
97 | This website demonstrates how you can use Adobe Target library AT.js to author and deliver experiences on
98 | websites built with Single Page Apps (SPAs).
99 |