diff --git a/modules/geoedgeRtdProvider.js b/modules/geoedgeRtdProvider.js
index 6f910632fbc..fdd7aa2f5eb 100644
--- a/modules/geoedgeRtdProvider.js
+++ b/modules/geoedgeRtdProvider.js
@@ -20,6 +20,8 @@ import { ajax } from '../src/ajax.js';
import { generateUUID, insertElement, isEmpty, logError } from '../src/utils.js';
import * as events from '../src/events.js';
import CONSTANTS from '../src/constants.json';
+import { loadExternalScript } from '../src/adloader.js';
+import { auctionManager } from '../src/auctionManager.js';
/** @type {string} */
const SUBMODULE_NAME = 'geoedge';
@@ -33,9 +35,13 @@ const PV_ID = generateUUID();
/** @type {string} */
const HOST_NAME = 'https://rumcdn.geoedge.be';
/** @type {string} */
-const FILE_NAME = 'grumi.js';
+const FILE_NAME_CLIENT = 'grumi.js';
+/** @type {string} */
+const FILE_NAME_INPAGE = 'grumi-ip.js';
+/** @type {function} */
+export let getClientUrl = (key) => `${HOST_NAME}/${key}/${FILE_NAME_CLIENT}`;
/** @type {function} */
-export let getClientUrl = (key) => `${HOST_NAME}/${key}/${FILE_NAME}`;
+export let getInPageUrl = (key) => `${HOST_NAME}/${key}/${FILE_NAME_INPAGE}`;
/** @type {string} */
export let wrapper
/** @type {boolean} */;
@@ -177,7 +183,8 @@ function isSupportedBidder(bidder, paramsBidders) {
function shouldWrap(bid, params) {
let supportedBidder = isSupportedBidder(bid.bidderCode, params.bidders);
let donePreload = params.wap ? preloaded : true;
- return wrapperReady && supportedBidder && donePreload;
+ let isGPT = params.gpt;
+ return wrapperReady && supportedBidder && donePreload && !isGPT;
}
function conditionallyWrap(bidResponse, config, userConsent) {
@@ -187,31 +194,55 @@ function conditionallyWrap(bidResponse, config, userConsent) {
}
}
+function isBillingMessage(data, params) {
+ return data.key === params.key && data.impression;
+}
+
/**
- * Fire billable events for applicable bids
+ * Fire billable events when our client sends a message
+ * Messages will be sent only when:
+ * a. applicable bids are wrapped
+ * b. our code laoded and executed sucesfully
*/
function fireBillableEventsForApplicableBids(params) {
- events.on(CONSTANTS.EVENTS.BID_WON, function (winningBid) {
- if (shouldWrap(winningBid, params)) {
+ window.addEventListener('message', function (message) {
+ let data = message.data;
+ if (isBillingMessage(data, params)) {
+ let winningBid = auctionManager.findBidByAdId(data.adId);
events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, {
vendor: SUBMODULE_NAME,
- billingId: generateUUID(),
- type: 'impression',
- transactionId: winningBid.transactionId,
- auctionId: winningBid.auctionId,
- bidId: winningBid.requestId
+ billingId: data.impressionId,
+ type: winningBid ? 'impression' : data.type,
+ transactionId: winningBid?.transactionId || data.transactionId,
+ auctionId: winningBid?.auctionId || data.auctionId,
+ bidId: winningBid?.requestId || data.requestId
});
}
});
}
+/**
+ * Loads Geoedge in page script that monitors all ad slots created by GPT
+ * @param {Object} params
+ */
+function setupInPage(params) {
+ window.grumi = params;
+ window.grumi.fromPrebid = true;
+ loadExternalScript(getInPageUrl(params.key), SUBMODULE_NAME);
+}
+
function init(config, userConsent) {
let params = config.params;
if (!params || !params.key) {
logError('missing key for geoedge RTD module provider');
return false;
}
- preloadClient(params.key);
+ if (params.gpt) {
+ setupInPage(params);
+ } else {
+ fetchWrapper(setWrapper);
+ preloadClient(params.key);
+ }
fireBillableEventsForApplicableBids(params);
return true;
}
@@ -228,7 +259,6 @@ export const geoedgeSubmodule = {
};
export function beforeInit() {
- fetchWrapper(setWrapper);
submodule('realTimeData', geoedgeSubmodule);
}
diff --git a/modules/geoedgeRtdProvider.md b/modules/geoedgeRtdProvider.md
index 5414606612c..cdf913b8893 100644
--- a/modules/geoedgeRtdProvider.md
+++ b/modules/geoedgeRtdProvider.md
@@ -5,7 +5,7 @@ Module Type: Rtd Provider
Maintainer: guy.books@geoedge.com
The Geoedge Realtime module lets publishers block bad ads such as automatic redirects, malware, offensive creatives and landing pages.
-To use this module, you'll need to work with [Geoedge](https://www.geoedge.com/publishers-real-time-protection/) to get an account and cutomer key.
+To use this module, you'll need to work with [Geoedge](https://www.geoedge.com/publishers-real-time-protection/) to get an account and customer key.
## Integration
@@ -49,6 +49,7 @@ Parameters details:
|params.key | String | Customer key |Required, contact Geoedge to get your key |
|params.bidders | Object | Bidders to monitor |Optional, list of bidder to include / exclude from monitoring. Omitting this will monitor bids from all bidders. |
|params.wap |Boolean |Wrap after preload |Optional, defaults to `false`. Set to `true` if you want to monitor only after the module has preloaded the monitoring client. |
+|params.gpt |Boolean |Wrap all GPT ad slots |Optional, defaults to `false`. Set to `true` if you want to monitor all Google Publisher Tag ad slots, regaedless if the winning bid comes from Prebid or Google Ad Manager (Direct, Adx, Adesnse, Open Bidding, etc). |
## Example
diff --git a/src/adloader.js b/src/adloader.js
index fb4aa44e872..a87b930b7df 100644
--- a/src/adloader.js
+++ b/src/adloader.js
@@ -26,6 +26,7 @@ const _approvedLoadExternalJSList = [
'airgrid',
'clean.io',
'a1Media',
+ 'geoedge',
]
/**
diff --git a/test/spec/modules/geoedgeRtdProvider_spec.js b/test/spec/modules/geoedgeRtdProvider_spec.js
index eec1feff87a..2f2fc8e2775 100644
--- a/test/spec/modules/geoedgeRtdProvider_spec.js
+++ b/test/spec/modules/geoedgeRtdProvider_spec.js
@@ -1,12 +1,13 @@
import * as utils from '../../../src/utils.js';
+import { loadExternalScript } from '../../../src/adloader.js';
import * as hook from '../../../src/hook.js'
-import { beforeInit, geoedgeSubmodule, setWrapper, wrapper, htmlPlaceholder, WRAPPER_URL, getClientUrl } from '../../../modules/geoedgeRtdProvider.js';
+import { beforeInit, geoedgeSubmodule, setWrapper, wrapper, htmlPlaceholder, WRAPPER_URL, getClientUrl, getInPageUrl } from '../../../modules/geoedgeRtdProvider.js';
import { server } from '../../../test/mocks/xhr.js';
import * as events from '../../../src/events.js';
import CONSTANTS from '../../../src/constants.json';
let key = '123123123';
-function makeConfig() {
+function makeConfig(gpt) {
return {
name: 'geoedge',
params: {
@@ -15,7 +16,8 @@ function makeConfig() {
bidders: {
bidderA: true,
bidderB: false
- }
+ },
+ gpt: gpt
}
};
}
@@ -23,6 +25,7 @@ function makeConfig() {
function mockBid(bidderCode) {
return {
'ad': '',
+ 'adId': '1234',
'cpm': '1.00',
'width': 300,
'height': 250,
@@ -35,6 +38,15 @@ function mockBid(bidderCode) {
};
}
+function mockMessageFromClient(key) {
+ return {
+ key,
+ impression: true,
+ adId: 1234,
+ type: 'impression'
+ };
+}
+
let mockWrapper = `${htmlPlaceholder}`;
describe('Geoedge RTD module', function () {
@@ -47,22 +59,11 @@ describe('Geoedge RTD module', function () {
after(function () {
submoduleStub.restore();
});
- it('should fetch the wrapper', function () {
- beforeInit();
- let request = server.requests[0];
- let isWrapperRequest = request && request.url && request.url && request.url === WRAPPER_URL;
- expect(isWrapperRequest).to.equal(true);
- });
it('should register RTD submodule provider', function () {
+ beforeInit();
expect(submoduleStub.calledWith('realTimeData', geoedgeSubmodule)).to.equal(true);
});
});
- describe('setWrapper', function () {
- it('should set the wrapper', function () {
- setWrapper(mockWrapper);
- expect(wrapper).to.equal(mockWrapper);
- });
- });
describe('submodule', function () {
describe('name', function () {
it('should be geoedge', function () {
@@ -84,35 +85,54 @@ describe('Geoedge RTD module', function () {
expect(missingParams || missingKey).to.equal(false);
});
it('should return true when params are ok', function () {
- expect(geoedgeSubmodule.init(makeConfig())).to.equal(true);
+ expect(geoedgeSubmodule.init(makeConfig(false))).to.equal(true);
+ });
+ it('should fetch the wrapper', function () {
+ geoedgeSubmodule.init(makeConfig(false));
+ let request = server.requests[0];
+ let isWrapperRequest = request && request.url && request.url && request.url === WRAPPER_URL;
+ expect(isWrapperRequest).to.equal(true);
});
it('should preload the client', function () {
let isLinkPreloadAsScript = arg => arg.tagName === 'LINK' && arg.rel === 'preload' && arg.as === 'script' && arg.href === getClientUrl(key);
expect(insertElementStub.calledWith(sinon.match(isLinkPreloadAsScript))).to.equal(true);
});
- it('should emit billable events with applicable winning bids', function () {
- let applicableBid = mockBid('bidderA');
- let nonApplicableBid = mockBid('bidderB');
+ it('should emit billable events with applicable winning bids', function (done) {
let counter = 0;
events.on(CONSTANTS.EVENTS.BILLABLE_EVENT, function (event) {
if (event.vendor === 'geoedge' && event.type === 'impression') {
counter += 1;
}
+ expect(counter).to.equal(1);
+ done();
});
- events.emit(CONSTANTS.EVENTS.BID_WON, applicableBid);
- events.emit(CONSTANTS.EVENTS.BID_WON, nonApplicableBid);
- expect(counter).to.equal(1);
+ window.postMessage(mockMessageFromClient(key), '*');
+ });
+ it('should load the in page code when gpt params is true', function () {
+ geoedgeSubmodule.init(makeConfig(true));
+ let isInPageUrl = arg => arg == getInPageUrl(key);
+ expect(loadExternalScript.calledWith(sinon.match(isInPageUrl))).to.equal(true);
+ });
+ it('should set the window.grumi config object when gpt params is true', function () {
+ let hasGrumiObj = typeof window.grumi === 'object';
+ expect(hasGrumiObj && window.grumi.key === key && window.grumi.fromPrebid).to.equal(true);
+ });
+ });
+ describe('setWrapper', function () {
+ it('should set the wrapper', function () {
+ setWrapper(mockWrapper);
+ expect(wrapper).to.equal(mockWrapper);
});
});
describe('onBidResponseEvent', function () {
let bidFromA = mockBid('bidderA');
it('should wrap bid html when bidder is configured', function () {
- geoedgeSubmodule.onBidResponseEvent(bidFromA, makeConfig());
+ geoedgeSubmodule.onBidResponseEvent(bidFromA, makeConfig(false));
expect(bidFromA.ad.indexOf('')).to.equal(0);
});
it('should not wrap bid html when bidder is not configured', function () {
let bidFromB = mockBid('bidderB');
- geoedgeSubmodule.onBidResponseEvent(bidFromB, makeConfig());
+ geoedgeSubmodule.onBidResponseEvent(bidFromB, makeConfig(false));
expect(bidFromB.ad.indexOf('')).to.equal(-1);
});
it('should only muatate the bid ad porperty', function () {