diff --git a/README.md b/README.md index 90c8ae16..11d687b7 100644 --- a/README.md +++ b/README.md @@ -918,32 +918,32 @@ The Optable Web SDK provides a method `installGPTSecureSignals` to pass user-def Please refer to the list of approved Secure Signal [providers](https://support.google.com/admanager/answer/14750072). Please refer to the provider's integration documentation for the exact provider name and value to pass as a signal. -To build the signals from cached EIDs instead of by hand, `secureSignalsFromEids(eids, filter?)` builds one `{ provider, id }` pair per EID source — GPT keys secure signals by provider, so the first EID that passes the filter and carries a non-empty uid wins its source — optionally filtered by `sources`, `inserters` or `matchers` (an empty or missing list means no constraint on that field). +To build the signals from EIDs instead of by hand, `installGPTSecureSignalsFromEids({ filter?, eids? })` installs one signal per EID source — GPT keys secure signals by provider, so the first EID that passes the filter and carries a non-empty uid wins its source. `filter` narrows by `sources`, `inserters` or `matchers` (an empty or missing list means no constraint on that field). -The signals come from the last cached targeting response, so a first visit installs none until `targeting()` has resolved once. +`eids` defaults to the last cached targeting response, so a first visit installs none until `targeting()` has resolved once. Pass it explicitly to install from somewhere else. With the npm package: ```javascript -import { secureSignalsFromEids } from "@optable/web-sdk/lib/dist/core/secure-signals"; +import "@optable/web-sdk/lib/dist/addons/gpt"; -const eids = sdk.targetingFromCache()?.ortb2?.user?.eids ?? []; -sdk.installGPTSecureSignals(...secureSignalsFromEids(eids, { sources: ["uidapi.com"] })); +sdk.installGPTSecureSignalsFromEids({ filter: { sources: ["uidapi.com"] } }); ``` -With the SDK loaded from a script tag, the helper is available under `optable.utils`: +With the SDK loaded from a script tag: ```html ``` +`secureSignalsFromEids(eids, filter?)` is also exported from `@optable/web-sdk/lib/dist/core/secure-signals`, and available as `optable.utils.secureSignalsFromEids` on script-tag pages, for callers that want the `{ provider, id }` pairs without installing them. + You can verify the signal was correctly passed to GAM by searching for its value cached in `localStorage` under the key `_GESPSK-`. ## Integrating Prebid diff --git a/lib/addons/gpt.test.js b/lib/addons/gpt.test.js index 35c7f09b..8e8f5365 100644 --- a/lib/addons/gpt.test.js +++ b/lib/addons/gpt.test.js @@ -53,6 +53,46 @@ describe("OptableSDK - installGPTSecureSignals", () => { expect(window.googletag.secureSignalProviders).toHaveLength(0); }); + test("installs from the cached targeting response, applying the filter", () => { + jest.spyOn(SDK, "targetingFromCache").mockReturnValue({ + ortb2: { + user: { + eids: [ + { source: "uidapi.com", uids: [{ id: "uid2-a" }] }, + { source: "id5-sync.com", uids: [{ id: "id5-a" }] }, + ], + }, + }, + }); + + SDK.installGPTSecureSignalsFromEids({ filter: { sources: ["uidapi.com"] } }); + window.googletag.cmd.forEach((cmd) => cmd()); + + expect(window.googletag.secureSignalProviders).toEqual([ + { id: "uidapi.com", collectorFunction: expect.any(Function) }, + ]); + }); + + test("prefers explicitly passed eids over the cache", () => { + jest.spyOn(SDK, "targetingFromCache").mockReturnValue({ + ortb2: { user: { eids: [{ source: "cached.com", uids: [{ id: "cached" }] }] } }, + }); + + SDK.installGPTSecureSignalsFromEids({ eids: [{ source: "given.com", uids: [{ id: "given" }] }] }); + window.googletag.cmd.forEach((cmd) => cmd()); + + expect(window.googletag.secureSignalProviders.map((p) => p.id)).toEqual(["given.com"]); + }); + + test("installs nothing when the targeting cache is empty", () => { + jest.spyOn(SDK, "targetingFromCache").mockReturnValue(null); + + SDK.installGPTSecureSignalsFromEids(); + + expect(window.googletag.cmd).toHaveLength(0); + expect(window.googletag.secureSignalProviders).toHaveLength(0); + }); + test("handles an empty signals array gracefully", () => { SDK.installGPTSecureSignals(); diff --git a/lib/addons/gpt.ts b/lib/addons/gpt.ts index ba450e20..72988acb 100644 --- a/lib/addons/gpt.ts +++ b/lib/addons/gpt.ts @@ -1,6 +1,7 @@ import type { ContextualTargetingKeyValuesOptions } from "../edge/contextual_segments"; import type { WitnessProperties } from "../edge/witness"; -import type { SecureSignal } from "../core/secure-signals"; +import { secureSignalsFromEids } from "../core/secure-signals"; +import type { SecureSignal, SecureSignalEid, SecureSignalsFilter } from "../core/secure-signals"; import { debugLog } from "../core/log"; import OptableSDK from "../sdk"; @@ -8,6 +9,7 @@ declare module "../sdk" { export interface OptableSDK { installGPTEventListeners: () => void; installGPTSecureSignals: (...signals: SecureSignal[]) => void; + installGPTSecureSignalsFromEids: (options?: SecureSignalsFromEidsOptions) => void; } } @@ -84,6 +86,12 @@ OptableSDK.prototype.installGPTEventListeners = function (eventSpec?: GptEventSp }); }; +type SecureSignalsFromEidsOptions = { + filter?: SecureSignalsFilter; + // Defaults to the EIDs in the last cached targeting response. + eids?: SecureSignalEid[]; +}; + /* * Pass user-defined signals to GAM Secure Signals */ @@ -103,6 +111,13 @@ OptableSDK.prototype.installGPTSecureSignals = function (...signals: SecureSigna } }; +// Installs secure signals built from EIDs, defaulting to the cached targeting +// response. A first visit installs none until targeting() has resolved once. +OptableSDK.prototype.installGPTSecureSignalsFromEids = function (options: SecureSignalsFromEidsOptions = {}) { + const eids = options.eids ?? this.targetingFromCache()?.ortb2?.user?.eids ?? []; + this.installGPTSecureSignals(...secureSignalsFromEids(eids, options.filter ?? {})); +}; + // Fetches the page's contextual segments and pushes the resulting key-values // to GAM via googletag.pubads().setTargeting. taxonomyKeys and options are // forwarded to ctxTargetingKeyValues(); url to ctxSegments(), for SPAs @@ -140,3 +155,5 @@ export async function setContextualTargetingInGAM( } }); } + +export type { SecureSignalsFromEidsOptions };