diff --git a/integrationExamples/gpt/growthcode.html b/integrationExamples/gpt/growthcode.html new file mode 100644 index 00000000000..ede51d2d869 --- /dev/null +++ b/integrationExamples/gpt/growthcode.html @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + +

Prebid.js Test

+
Div-1
+
+ +
+ + diff --git a/modules/growthCodeAnalyticsAdapter.js b/modules/growthCodeAnalyticsAdapter.js new file mode 100644 index 00000000000..1f11b891139 --- /dev/null +++ b/modules/growthCodeAnalyticsAdapter.js @@ -0,0 +1,176 @@ +/** + * growthCodeAnalyticsAdapter.js - GrowthCode Analytics Adapter + */ +import { ajax } from '../src/ajax.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; +import adapterManager from '../src/adapterManager.js'; +import * as utils from '../src/utils.js'; +import CONSTANTS from '../src/constants.json'; +import { getStorageManager } from '../src/storageManager.js'; +import {getRefererInfo} from '../src/refererDetection.js'; +import {logError, logInfo} from '../src/utils.js'; + +const MODULE_NAME = 'growthCodeAnalytics'; +const DEFAULT_PID = 'INVALID_PID' +const ENDPOINT_URL = 'https://p2.gcprivacy.com/v1/pb/analytics' + +export const storage = getStorageManager(); + +let sessionId = utils.generateUUID(); + +let trackEvents = []; +let pid = DEFAULT_PID; +let url = ENDPOINT_URL; + +let eventQueue = []; + +let startAuction = 0; +let bidRequestTimeout = 0; +let analyticsType = 'endpoint'; + +let growthCodeAnalyticsAdapter = Object.assign(adapter({url: url, analyticsType}), { + track({eventType, eventData}) { + eventData = eventData ? JSON.parse(JSON.stringify(eventData)) : {}; + let data = {}; + if (!trackEvents.includes(eventType)) return; + switch (eventType) { + case CONSTANTS.EVENTS.AUCTION_INIT: { + data = eventData; + startAuction = data.timestamp; + bidRequestTimeout = data.timeout; + break; + } + + case CONSTANTS.EVENTS.AUCTION_END: { + data = eventData; + data.start = startAuction; + data.end = Date.now(); + break; + } + + case CONSTANTS.EVENTS.BID_ADJUSTMENT: { + data.bidders = eventData; + break; + } + + case CONSTANTS.EVENTS.BID_TIMEOUT: { + data.bidders = eventData; + data.duration = bidRequestTimeout; + break; + } + + case CONSTANTS.EVENTS.BID_REQUESTED: { + data = eventData; + break; + } + + case CONSTANTS.EVENTS.BID_RESPONSE: { + data = eventData; + delete data.ad; + break; + } + + case CONSTANTS.EVENTS.BID_WON: { + data = eventData; + delete data.ad; + delete data.adUrl; + break; + } + + case CONSTANTS.EVENTS.BIDDER_DONE: { + data = eventData; + break; + } + + case CONSTANTS.EVENTS.SET_TARGETING: { + data.targetings = eventData; + break; + } + + case CONSTANTS.EVENTS.REQUEST_BIDS: { + data = eventData; + break; + } + + case CONSTANTS.EVENTS.ADD_AD_UNITS: { + data = eventData; + break; + } + + default: + return; + } + + data.eventType = eventType; + data.timestamp = data.timestamp || Date.now(); + + sendEvent(data); + } +}); + +growthCodeAnalyticsAdapter.originEnableAnalytics = growthCodeAnalyticsAdapter.enableAnalytics; + +growthCodeAnalyticsAdapter.enableAnalytics = function(conf = {}) { + if (typeof conf.options === 'object') { + if (conf.options.pid) { + pid = conf.options.pid; + url = conf.options.url ? conf.options.url : ENDPOINT_URL; + } else { + logError(MODULE_NAME + ' Not a valid PartnerID') + return + } + if (conf.options.trackEvents) { + trackEvents = conf.options.trackEvents; + } + } else { + logError(MODULE_NAME + ' Invalid configuration'); + return; + } + + growthCodeAnalyticsAdapter.originEnableAnalytics(conf); +}; + +function logToServer() { + if (pid === DEFAULT_PID) return; + if (eventQueue.length > 1) { + let data = { + session: sessionId, + pid: pid, + timestamp: Date.now(), + timezoneoffset: new Date().getTimezoneOffset(), + url: getRefererInfo().page, + referer: document.referrer, + events: eventQueue + }; + + ajax(url, { + success: response => { + logInfo(MODULE_NAME + ' Send Data to Server') + }, + error: error => { + logInfo(MODULE_NAME + ' Problem Send Data to Server: ' + error) + } + }, JSON.stringify(data), {method: 'POST', withCredentials: true}) + + eventQueue = [ + ]; + } +} + +function sendEvent(event) { + eventQueue.push(event); + logInfo(MODULE_NAME + 'Analytics Event: ' + event); + + if (event.eventType === CONSTANTS.EVENTS.AUCTION_END) { + logToServer(); + } +} + +adapterManager.registerAnalyticsAdapter({ + adapter: growthCodeAnalyticsAdapter, + code: 'growthCodeAnalytics' +}); + +growthCodeAnalyticsAdapter.logToServer = logToServer; + +export default growthCodeAnalyticsAdapter; diff --git a/modules/growthCodeAnalyticsAdapter.md b/modules/growthCodeAnalyticsAdapter.md new file mode 100644 index 00000000000..e45cb2e9c62 --- /dev/null +++ b/modules/growthCodeAnalyticsAdapter.md @@ -0,0 +1,41 @@ +## GrowthCode Analytics Adapter + +[GrowthCode](https://growthcode.io) offers scaled infrastructure-as-a-service to +empower independent publishers to harness data and take control of identity and +audience while rapidly aligning to industry changes and margin pressure. + +## Building Prebid with GrowthCode Support + +First, make sure to add the GrowthCode submodule to your Prebid.js package with: + +``` +gulp build --modules=growthCodeIdSystem,growthCodeAnalyticsAdapter,userId +``` + +The following configuration parameters are available: + +```javascript +pbjs.enableAnalytics({ + provider: 'growthCodeAnalytics', + options: { + pid: '', + trackEvents: [ + 'auctionEnd', + 'bidAdjustment', + 'bidTimeout', + 'bidRequested', + 'bidResponse', + 'noBid', + 'bidWon', + 'bidderDone'] + } +}); +``` + +| Param enableAnalytics | Scope | Type | Description | Example | +|-----------------------|----------|--------|-------------------------------------------------------------|--------------------------| +| provider | Required | String | The name of this Adapter. | `"growthCodeAnalytics"` | +| params | Required | Object | Details of module params. | | +| params.pid | Required | String | This is the Customer ID value obtained via Intimate Merger. | `""` | +| params.url | Optional | String | Custom URL for server | | +| params.trackEvents | Required | String | Name if the variable that holds your publisher ID | | diff --git a/test/spec/modules/growthCodeAnalyticsAdapter_spec.js b/test/spec/modules/growthCodeAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..e542a2641e8 --- /dev/null +++ b/test/spec/modules/growthCodeAnalyticsAdapter_spec.js @@ -0,0 +1,70 @@ +import adapterManager from '../../../src/adapterManager.js'; +import growthCodeAnalyticsAdapter from '../../../modules/growthCodeAnalyticsAdapter.js'; +import { expect } from 'chai'; +import * as events from '../../../src/events.js'; +import constants from '../../../src/constants.json'; +import { generateUUID } from '../../../src/utils.js'; +import { server } from 'test/mocks/xhr.js'; + +describe('growthCode analytics adapter', () => { + beforeEach(() => { + growthCodeAnalyticsAdapter.enableAnalytics({ + provider: 'growthCodeAnalytics', + options: { + pid: 'TEST01', + trackEvents: [ + 'auctionInit', + 'auctionEnd', + 'bidAdjustment', + 'bidTimeout', + 'bidTimeout', + 'bidRequested', + 'bidResponse', + 'setTargeting', + 'requestBids', + 'addAdUnits', + 'noBid', + 'bidWon', + 'bidderDone'] + } + }); + }); + + afterEach(() => { + growthCodeAnalyticsAdapter.disableAnalytics(); + }); + + it('registers itself with the adapter manager', () => { + const adapter = adapterManager.getAnalyticsAdapter('growthCodeAnalytics'); + expect(adapter).to.exist; + expect(adapter.adapter).to.equal(growthCodeAnalyticsAdapter); + }); + + it('tolerates undefined or empty config', () => { + growthCodeAnalyticsAdapter.enableAnalytics(undefined); + growthCodeAnalyticsAdapter.enableAnalytics({}); + }); + + it('sends auction end events to the backend', () => { + const auction = { + auctionId: generateUUID(), + adUnits: [{ + code: 'usr1234', + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600], [728, 90]] + } + }, + adUnitCodes: ['usr1234'] + }], + }; + events.emit(constants.EVENTS.AUCTION_END, auction); + assert(server.requests.length > 0) + const body = JSON.parse(server.requests[0].requestBody); + var eventTypes = []; + body.events.forEach(e => eventTypes.push(e.eventType)); + assert(eventTypes.length > 0) + assert(eventTypes.indexOf(constants.EVENTS.AUCTION_END) > -1); + growthCodeAnalyticsAdapter.disableAnalytics(); + }); +});