Skip to content

Commit 3958012

Browse files
committed
Merge branch 'appnexus_dchain_start'
2 parents 34a7ce0 + 85b01fd commit 3958012

File tree

5 files changed

+548
-0
lines changed

5 files changed

+548
-0
lines changed

modules/appnexusBidAdapter.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,22 @@ function newBid(serverBid, rtbBid, bidderRequest) {
599599
bid.meta = Object.assign({}, bid.meta, { advertiserId: rtbBid.advertiser_id });
600600
}
601601

602+
// temporary function; may remove at later date if/when adserver fully supports dchain
603+
function setupDChain(rtbBid) {
604+
let dchain = {
605+
ver: '1.0',
606+
complete: 0,
607+
nodes: [{
608+
bsid: rtbBid.buyer_member_id.toString()
609+
}],
610+
};
611+
612+
return dchain;
613+
}
614+
if (rtbBid.buyer_member_id) {
615+
bid.meta = Object.assign({}, bid.meta, {dchain: setupDChain(rtbBid)});
616+
}
617+
602618
if (rtbBid.brand_id) {
603619
bid.meta = Object.assign({}, bid.meta, { brandId: rtbBid.brand_id });
604620
}

modules/dchain.js

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import includes from 'core-js-pure/features/array/includes.js';
2+
import { config } from '../src/config.js';
3+
import { getHook } from '../src/hook.js';
4+
import { _each, isStr, isArray, isPlainObject, hasOwn, deepClone, deepAccess, logWarn, logError } from '../src/utils.js';
5+
6+
const shouldBeAString = ' should be a string';
7+
const shouldBeAnObject = ' should be an object';
8+
const shouldBeAnArray = ' should be an Array';
9+
const shouldBeValid = ' is not a valid dchain property';
10+
const MODE = {
11+
STRICT: 'strict',
12+
RELAXED: 'relaxed',
13+
OFF: 'off'
14+
};
15+
const MODES = []; // an array of modes
16+
_each(MODE, mode => MODES.push(mode));
17+
18+
export function checkDchainSyntax(bid, mode) {
19+
let dchainObj = deepClone(bid.meta.dchain);
20+
let failPrefix = 'Detected something wrong in bid.meta.dchain object for bid:';
21+
let failMsg = '';
22+
const dchainPropList = ['ver', 'complete', 'nodes', 'ext'];
23+
24+
function appendFailMsg(msg) {
25+
failMsg += '\n' + msg;
26+
}
27+
28+
function printFailMsg() {
29+
if (mode === MODE.STRICT) {
30+
logError(failPrefix, bid, '\n', dchainObj, failMsg);
31+
} else {
32+
logWarn(failPrefix, bid, `\n`, dchainObj, failMsg);
33+
}
34+
}
35+
36+
let dchainProps = Object.keys(dchainObj);
37+
dchainProps.forEach(prop => {
38+
if (!includes(dchainPropList, prop)) {
39+
appendFailMsg(`dchain.${prop}` + shouldBeValid);
40+
}
41+
});
42+
43+
if (dchainObj.complete !== 0 && dchainObj.complete !== 1) {
44+
appendFailMsg(`dchain.complete should be 0 or 1`);
45+
}
46+
47+
if (!isStr(dchainObj.ver)) {
48+
appendFailMsg(`dchain.ver` + shouldBeAString);
49+
}
50+
51+
if (hasOwn(dchainObj, 'ext')) {
52+
if (!isPlainObject(dchainObj.ext)) {
53+
appendFailMsg(`dchain.ext` + shouldBeAnObject);
54+
}
55+
}
56+
57+
if (!isArray(dchainObj.nodes)) {
58+
appendFailMsg(`dchain.nodes` + shouldBeAnArray);
59+
printFailMsg();
60+
if (mode === MODE.STRICT) return false;
61+
} else {
62+
const nodesPropList = ['asi', 'bsid', 'rid', 'name', 'domain', 'ext'];
63+
dchainObj.nodes.forEach((node, index) => {
64+
if (!isPlainObject(node)) {
65+
appendFailMsg(`dchain.nodes[${index}]` + shouldBeAnObject);
66+
} else {
67+
let nodeProps = Object.keys(node);
68+
nodeProps.forEach(prop => {
69+
if (!includes(nodesPropList, prop)) {
70+
appendFailMsg(`dchain.nodes[${index}].${prop}` + shouldBeValid);
71+
}
72+
73+
if (prop === 'ext') {
74+
if (!isPlainObject(node.ext)) {
75+
appendFailMsg(`dchain.nodes[${index}].ext` + shouldBeAnObject);
76+
}
77+
} else {
78+
if (!isStr(node[prop])) {
79+
appendFailMsg(`dchain.nodes[${index}].${prop}` + shouldBeAString);
80+
}
81+
}
82+
});
83+
}
84+
});
85+
}
86+
87+
if (failMsg.length > 0) {
88+
printFailMsg();
89+
if (mode === MODE.STRICT) {
90+
return false;
91+
}
92+
}
93+
return true;
94+
}
95+
96+
function isValidDchain(bid) {
97+
let mode = MODE.STRICT;
98+
const dchainConfig = config.getConfig('dchain');
99+
100+
if (dchainConfig && isStr(dchainConfig.validation) && MODES.indexOf(dchainConfig.validation) != -1) {
101+
mode = dchainConfig.validation;
102+
}
103+
104+
if (mode === MODE.OFF) {
105+
return true;
106+
} else {
107+
return checkDchainSyntax(bid, mode);
108+
}
109+
}
110+
111+
export function addBidResponseHook(fn, adUnitCode, bid) {
112+
const basicDchain = {
113+
ver: '1.0',
114+
complete: 0,
115+
nodes: []
116+
};
117+
118+
if (deepAccess(bid, 'meta.networkId') && deepAccess(bid, 'meta.networkName')) {
119+
basicDchain.nodes.push({ name: bid.meta.networkName, bsid: bid.meta.networkId.toString() });
120+
}
121+
basicDchain.nodes.push({ name: bid.bidderCode });
122+
123+
let bidDchain = deepAccess(bid, 'meta.dchain');
124+
if (bidDchain && isPlainObject(bidDchain)) {
125+
let result = isValidDchain(bid);
126+
127+
if (result) {
128+
// extra check in-case mode is OFF and there is a setup issue
129+
if (isArray(bidDchain.nodes)) {
130+
bid.meta.dchain.nodes.push({ asi: bid.bidderCode });
131+
} else {
132+
logWarn('bid.meta.dchain.nodes did not exist or was not an array; did not append prebid dchain.', bid);
133+
}
134+
} else {
135+
// remove invalid dchain
136+
delete bid.meta.dchain;
137+
}
138+
} else {
139+
bid.meta.dchain = basicDchain;
140+
}
141+
142+
fn(adUnitCode, bid);
143+
}
144+
145+
export function init() {
146+
getHook('addBidResponse').before(addBidResponseHook, 35);
147+
}
148+
149+
init();

modules/dchain.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# dchain module
2+
3+
Refer:
4+
- https://iabtechlab.com/buyers-json-demand-chain/
5+
6+
## Sample code for dchain setConfig and dchain object
7+
```
8+
pbjs.setConfig({
9+
"dchain": {
10+
"validation": "strict"
11+
}
12+
});
13+
```
14+
15+
```
16+
bid.meta.dchain: {
17+
"complete": 0,
18+
"ver": "1.0",
19+
"ext": {...},
20+
"nodes": [{
21+
"asi": "abc",
22+
"bsid": "123",
23+
"rid": "d4e5f6",
24+
"name": "xyz",
25+
"domain": "mno",
26+
"ext": {...}
27+
}, ...]
28+
}
29+
```
30+
31+
## Workflow
32+
The dchain module is not enabled by default as it may not be necessary for all publishers.
33+
If required, dchain module can be included as following
34+
```
35+
$ gulp build --modules=dchain,pubmaticBidAdapter,openxBidAdapter,rubiconBidAdapter,sovrnBidAdapter
36+
```
37+
38+
The dchain module will validate a bidder's dchain object (if it was defined). Bidders should assign their dchain object into `bid.meta` field. If the dchain object is valid, it will remain in the bid object for later use.
39+
40+
If it was not defined, the dchain will create a default dchain object for prebid.
41+
42+
## Validation modes
43+
- ```strict```: It is the default validation mode. In this mode, dchain object will not be accpeted from adapters if it is invalid. Errors are thrown for invalid dchain object.
44+
- ```relaxed```: In this mode, errors are thrown for an invalid dchain object but the invalid dchain object is still accepted from adapters.
45+
- ```off```: In this mode, no validations are performed and dchain object is accepted as is from adapters.

test/spec/modules/appnexusBidAdapter_spec.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,15 @@ describe('AppNexusAdapter', function () {
10811081
'adUnitCode': 'code',
10821082
'appnexus': {
10831083
'buyerMemberId': 958
1084+
},
1085+
'meta': {
1086+
'dchain': {
1087+
'ver': '1.0',
1088+
'complete': 0,
1089+
'nodes': [{
1090+
'bsid': '958'
1091+
}]
1092+
}
10841093
}
10851094
}
10861095
];

0 commit comments

Comments
 (0)