From 707564c8aebd4ff988d80f63368abc107f2a0cc8 Mon Sep 17 00:00:00 2001 From: Bosko Milekic Date: Tue, 10 Nov 2020 11:07:24 -0500 Subject: [PATCH] targetingFromCache and targetingClearCache APIs (#23) * targetingFromCache and targetingClearCache APIs - targeting API now also caches returned keyvalues to localStorage - targetingFromCache will fetch previously cached keyvalues from localStorage. Contrary to the targeting API, it is synchronous - targetingClearCache will clear any previously cached keyvalues - Introduce demos/vanilla/targeting/gam360-cached.html.tpl to show usage of targetingFromCache - Update README with targeting*Cache docs --- README.md | 81 +++++++++++++ demos/Makefile | 1 + demos/index.html | 3 + demos/vanilla/targeting/gam360-cached.html | 107 ++++++++++++++++++ .../vanilla/targeting/gam360-cached.html.tpl | 107 ++++++++++++++++++ lib/core/storage.ts | 37 ++++++ lib/edge/targeting.ts | 24 +++- lib/sdk.ts | 14 ++- 8 files changed, 368 insertions(+), 6 deletions(-) create mode 100644 demos/vanilla/targeting/gam360-cached.html create mode 100644 demos/vanilla/targeting/gam360-cached.html.tpl create mode 100644 lib/core/storage.ts diff --git a/README.md b/README.md index dd7668f..47c2349 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ JavaScript SDK for integrating with optable-sandbox from a web site or web appli - [Using (script tag)](#using-script-tag) - [Integrating GAM360](#integrating-gam360) - [Targeting key values](#targeting-key-values) + - [Targeting key values from local cache](#targeting-key-values-from-local-cache) - [Witnessing ad events](#witnessing-ad-events) - [Identifying visitors arriving from Email newsletters](#identifying-visitors-arriving-from-email-newsletters) - [Insert oeid into your Email newsletter template](#insert-oeid-into-your-email-newsletter-template) @@ -136,6 +137,25 @@ sdk On success, the resulting key values are typically sent as part of a subsequent ad call. Therefore we recommend that you either call targeting() before each ad call, or in parallel periodically, caching the resulting key values which you then provide in ad calls. +#### Caching Targeting Data + +The `targeting` API will automatically cache resulting key value data in client storage on success. You can subsequently retrieve the cached key value data as follows: + +```{javascript +const cachedTargetingData = sdk.targetingFromCache(); +for (const [key, values] of Object.entries(cachedTargetingData)) { + console.log(`Targeting KV: ${key} = ${values.join(",")}`); +} +``` + +You can also clear the locally cached targeting data: + +```javascript +sdk.targetingClearCache(); +``` + +Note that both `targetingFromCache()` and `targetingClearCache()` are synchronous. + ### Witness API To send real-time event data from the user's browser to the sandbox for eventual audience assembly, you can call the witness API as follows: @@ -280,6 +300,67 @@ It's suggested to load the GAM banner view with an ad even when the call to your ``` +Note the use of `googletag.pubads().disableInitialLoad()` in the above example. This will disable GAM ads from loading until the call to `googletag.pubads().refresh()` from the `loadGAM()` function. + +### Targeting key values from local cache + +It's also possible to avoid disabling of the initial ad load by using the SDK's `targetingFromCache()` method instead as in the following example: + +```html + + + + + + + + + + +
+ + +``` + +Note that the above example fetches locally cached targeting key values and calls `googletag.pubads().setTargeting()` with them. Note also that the usual `targeting()` call is done as well, though its return value is ignored. This ensures that the local targeting cache is kept updated as activations are modified. + ### Witnessing ad events To automatically capture GPT [SlotRenderEndedEvent](https://developers.google.com/doubleclick-gpt/reference#googletag.events.slotrenderendedevent) and [ImpressionViewableEvent](https://developers.google.com/doubleclick-gpt/reference#googletag.events.impressionviewableevent) and send log data to your sandbox using the **witness API**, simply install GPT event listeners on the SDK instance as follows: diff --git a/demos/Makefile b/demos/Makefile index 2a830a1..ec20157 100644 --- a/demos/Makefile +++ b/demos/Makefile @@ -7,6 +7,7 @@ all: html react html: envsubst < ./vanilla/identify.html.tpl > ./vanilla/identify.html && \ envsubst < ./vanilla/targeting/gam360.html.tpl > ./vanilla/targeting/gam360.html && \ + envsubst < ./vanilla/targeting/gam360-cached.html.tpl > ./vanilla/targeting/gam360-cached.html && \ envsubst < ./publishers/www.lapresse.ca/index.html.tpl > ./publishers/www.lapresse.ca/index.html .PHONY: react diff --git a/demos/index.html b/demos/index.html index 4bc28aa..04c30da 100644 --- a/demos/index.html +++ b/demos/index.html @@ -12,6 +12,9 @@

Vanilla

  • Targeting API - GAM360
  • +
  • + Cached Targeting API - GAM360 +
  • ReactJS

    diff --git a/demos/vanilla/targeting/gam360-cached.html b/demos/vanilla/targeting/gam360-cached.html new file mode 100644 index 0000000..c3aa3c7 --- /dev/null +++ b/demos/vanilla/targeting/gam360-cached.html @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + +

    Google Ad Manager 360: Targeting API Integration

    + +

    web-sdk-demo-gam360/header-ad

    + +
    + +

    web-sdk-demo-gam360/box-ad

    + +
    + +

    web-sdk-demo-gam360/footer-ad

    + +
    + +
    + + + + + + diff --git a/demos/vanilla/targeting/gam360-cached.html.tpl b/demos/vanilla/targeting/gam360-cached.html.tpl new file mode 100644 index 0000000..d4323bb --- /dev/null +++ b/demos/vanilla/targeting/gam360-cached.html.tpl @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + +

    Google Ad Manager 360: Targeting API Integration

    + +

    web-sdk-demo-gam360/header-ad

    + +
    + +

    web-sdk-demo-gam360/box-ad

    + +
    + +

    web-sdk-demo-gam360/footer-ad

    + +
    + +
    + + + + + + diff --git a/lib/core/storage.ts b/lib/core/storage.ts new file mode 100644 index 0000000..15b73d2 --- /dev/null +++ b/lib/core/storage.ts @@ -0,0 +1,37 @@ +import type { OptableConfig } from "../config"; +import type { TargetingKeyValues } from "../edge/targeting"; + +function toBinary(str: string): string { + const codeUnits = new Uint16Array(str.length); + for (let i = 0; i < codeUnits.length; i++) { + codeUnits[i] = str.charCodeAt(i); + } + return String.fromCharCode(...new Uint8Array(codeUnits.buffer)); +} + +class LocalStorage { + private targetingKey: string; + + constructor(private Config: OptableConfig) { + const sfx = btoa(toBinary(`${this.Config.host}/${this.Config.site}`)); + this.targetingKey = "OPTABLE_TGT_" + sfx; + } + + getTargeting(): TargetingKeyValues | null { + const kvs = window.localStorage.getItem(this.targetingKey); + return kvs ? (JSON.parse(kvs) as TargetingKeyValues) : null; + } + + setTargeting(keyvalues: TargetingKeyValues) { + if (keyvalues) { + window.localStorage.setItem(this.targetingKey, JSON.stringify(keyvalues)); + } + } + + clearTargeting() { + window.localStorage.removeItem(this.targetingKey); + } +} + +export { LocalStorage }; +export default LocalStorage; diff --git a/lib/edge/targeting.ts b/lib/edge/targeting.ts index 747de40..9fa7271 100644 --- a/lib/edge/targeting.ts +++ b/lib/edge/targeting.ts @@ -1,18 +1,36 @@ import type { OptableConfig } from "../config"; import { fetch } from "../core/network"; +import { LocalStorage } from "../core/storage"; type TargetingKeyValues = { [key: string]: string[]; }; -function Targeting(config: OptableConfig): Promise { - return fetch("/targeting", config, { +async function Targeting(config: OptableConfig): Promise { + const response: TargetingKeyValues = await fetch("/targeting", config, { method: "GET", headers: { "Content-Type": "application/json", }, }); + + if (response) { + const ls = new LocalStorage(config); + ls.setTargeting(response); + } + + return response; +} + +function TargetingFromCache(config: OptableConfig): TargetingKeyValues | null { + const ls = new LocalStorage(config); + return ls.getTargeting(); +} + +function TargetingClearCache(config: OptableConfig) { + const ls = new LocalStorage(config); + ls.clearTargeting(); } -export { Targeting, TargetingKeyValues }; +export { Targeting, TargetingFromCache, TargetingClearCache, TargetingKeyValues }; export default Targeting; diff --git a/lib/sdk.ts b/lib/sdk.ts index 3e6915f..4b7ae55 100644 --- a/lib/sdk.ts +++ b/lib/sdk.ts @@ -1,9 +1,9 @@ import type { OptableConfig } from "./config"; import type { TargetingKeyValues } from "./edge/targeting"; import type { WitnessProperties } from "./edge/witness"; -import Identify from "./edge/identify"; -import Targeting from "./edge/targeting"; -import Witness from "./edge/witness"; +import { Identify } from "./edge/identify"; +import { Witness } from "./edge/witness"; +import { Targeting, TargetingFromCache, TargetingClearCache } from "./edge/targeting"; import { sha256 } from "js-sha256"; class OptableSDK { @@ -20,6 +20,14 @@ class OptableSDK { return Targeting(this.sandbox); } + targetingFromCache(): TargetingKeyValues | null { + return TargetingFromCache(this.sandbox); + } + + targetingClearCache() { + TargetingClearCache(this.sandbox); + } + witness(event: string, properties: WitnessProperties = {}): Promise { return Witness(this.sandbox, event, properties); }