Skip to content

Commit e04bfc2

Browse files
authored
Mobkoi Bidder Adapter: Initial Release (prebid#12647)
* Mobkoi Bidder Adapter: Initialise implementation * feat: max-970: Prebid.js Bidder Adapter: Retrieve Adapter Parameters from Bid Configuration Object (prebid#8) Configuration Object](https://mobkoi.atlassian.net/browse/MAX-970) At this stage, we are only focused on bid win events, so there is no need for analytics adapter integration yet. To streamline the publisher's configuration for our custom bid adapter integration, we retrieve adapter parameters directly from the bid configuration object instead of using "bidderConfiguration." updated bid adapter doc * feat: max-956: We need the placement ID from Tag and HB Connector to be past to the AdServer (prebid#9) ### [We need the placement ID from Tag and HB Connector to be past to the AdServer](https://mobkoi.atlassian.net/browse/MAX-956) * set user.id if available
1 parent ed011ea commit e04bfc2

File tree

3 files changed

+650
-0
lines changed

3 files changed

+650
-0
lines changed

modules/mobkoiBidAdapter.js

+282
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
import { ortbConverter } from '../libraries/ortbConverter/converter.js';
2+
import { registerBidder } from '../src/adapters/bidderFactory.js';
3+
import { BANNER } from '../src/mediaTypes.js';
4+
import { _each, replaceMacros, deepAccess, deepSetValue, logError } from '../src/utils.js';
5+
6+
const BIDDER_CODE = 'mobkoi';
7+
const GVL_ID = 898;
8+
export const DEFAULT_AD_SERVER_BASE_URL = 'https://adserver.maximus.mobkoi.com';
9+
10+
const PUBLISHER_PARAMS = {
11+
/**
12+
* !IMPORTANT: This value must match the value in mobkoiAnalyticsAdapter.js
13+
* The name of the parameter that the publisher can use to specify the ad server endpoint.
14+
*/
15+
PARAM_NAME_AD_SERVER_BASE_URL: 'adServerBaseUrl',
16+
PARAM_NAME_PUBLISHER_ID: 'publisherId',
17+
PARAM_NAME_PLACEMENT_ID: 'placementId',
18+
}
19+
20+
/**
21+
* The list of ORTB response fields that are used in the macros. Field
22+
* replacement is self-implemented in the adapter. Use dot-notated path for
23+
* nested fields. For example, 'ad.ext.adomain'. For more information, visit
24+
* https://www.npmjs.com/package/dset and https://www.npmjs.com/package/dlv.
25+
*/
26+
const ORTB_RESPONSE_FIELDS_SUPPORT_MACROS = ['adm', 'nurl', 'lurl'];
27+
28+
export const converter = ortbConverter({
29+
context: {
30+
netRevenue: true,
31+
ttl: 30,
32+
},
33+
request(buildRequest, imps, bidderRequest, context) {
34+
const ortbRequest = buildRequest(imps, bidderRequest, context);
35+
const prebidBidRequest = context.bidRequests[0];
36+
37+
ortbRequest.id = utils.getOrtbId(prebidBidRequest);
38+
deepSetValue(ortbRequest, 'site.publisher.id', utils.getPublisherId(prebidBidRequest));
39+
deepSetValue(ortbRequest, 'site.publisher.ext.adServerBaseUrl', utils.getAdServerEndpointBaseUrl(prebidBidRequest));
40+
// We only support one impression per request.
41+
deepSetValue(ortbRequest, 'imp.0.tagid', utils.getPlacementId(prebidBidRequest));
42+
deepSetValue(ortbRequest, 'user.id', context.bidRequests[0].userId?.mobkoiId || null);
43+
44+
return ortbRequest;
45+
},
46+
bidResponse(buildPrebidBidResponse, ortbBidResponse, context) {
47+
utils.replaceAllMacrosInPlace(ortbBidResponse, context);
48+
49+
const prebidBid = buildPrebidBidResponse(ortbBidResponse, context);
50+
utils.addCustomFieldsToPrebidBidResponse(prebidBid, ortbBidResponse);
51+
return prebidBid;
52+
},
53+
});
54+
55+
export const spec = {
56+
code: BIDDER_CODE,
57+
supportedMediaTypes: [BANNER],
58+
gvlid: GVL_ID,
59+
60+
/**
61+
* Determines whether or not the given bid request is valid.
62+
*/
63+
isBidRequestValid(bid) {
64+
if (
65+
!deepAccess(bid, `params.${PUBLISHER_PARAMS.PARAM_NAME_PUBLISHER_ID}`) &&
66+
!deepAccess(bid, 'ortb2.site.publisher.id')
67+
) {
68+
logError(`The ${PUBLISHER_PARAMS.PARAM_NAME_PUBLISHER_ID} field is required in the bid request. ` +
69+
'Please follow the setup guideline to set the publisher ID field.'
70+
);
71+
return false;
72+
}
73+
74+
if (
75+
!deepAccess(bid, `params.${PUBLISHER_PARAMS.PARAM_NAME_PLACEMENT_ID}`)
76+
) {
77+
logError(`The ${PUBLISHER_PARAMS.PARAM_NAME_PLACEMENT_ID} field is required in the bid request. ` +
78+
'Please follow the setup guideline to set the placement ID field.')
79+
return false;
80+
}
81+
82+
return true;
83+
},
84+
/**
85+
* Make a server request from the list of BidRequests.
86+
*/
87+
buildRequests(prebidBidRequests, prebidBidderRequest) {
88+
const adServerEndpoint = utils.getAdServerEndpointBaseUrl(prebidBidderRequest) + '/bid';
89+
90+
return {
91+
method: 'POST',
92+
url: adServerEndpoint,
93+
options: {
94+
contentType: 'application/json',
95+
},
96+
data: converter.toORTB({
97+
bidRequests: prebidBidRequests,
98+
bidderRequest: prebidBidderRequest
99+
}),
100+
};
101+
},
102+
/**
103+
* Unpack the response from the server into a list of bids.
104+
*/
105+
interpretResponse(serverResponse, customBidRequest) {
106+
if (!serverResponse.body) return [];
107+
108+
const responseBody = {...serverResponse.body, seatbid: serverResponse.body.seatbid};
109+
const prebidBidResponse = converter.fromORTB({
110+
request: customBidRequest.data,
111+
response: responseBody,
112+
});
113+
return prebidBidResponse.bids;
114+
},
115+
};
116+
117+
registerBidder(spec);
118+
119+
export const utils = {
120+
/**
121+
* !IMPORTANT: Make sure the implementation of this function matches getAdServerEndpointBaseUrl
122+
* in both adapters.
123+
* Obtain the Ad Server Base URL from the given Prebid object.
124+
* @param {*} bid Prebid Bidder Request Object or Prebid Bid Response/Request
125+
* or ORTB Request/Response Object
126+
* @returns {string} The Ad Server Base URL
127+
*/
128+
getAdServerEndpointBaseUrl (bid) {
129+
// Fields that would be automatically set if the publisher set it via pbjs.setBidderConfig.
130+
const ortbPath = `site.publisher.ext.${PUBLISHER_PARAMS.PARAM_NAME_AD_SERVER_BASE_URL}`;
131+
const prebidPath = `ortb2.${ortbPath}`;
132+
133+
// Fields that would be set by the publisher in the bid
134+
// configuration object in ad unit.
135+
const paramPath = `params.${PUBLISHER_PARAMS.PARAM_NAME_AD_SERVER_BASE_URL}`;
136+
const bidRequestFirstBidParam = `bids.0.${paramPath}`;
137+
138+
const adServerBaseUrl =
139+
deepAccess(bid, paramPath) ||
140+
deepAccess(bid, bidRequestFirstBidParam) ||
141+
deepAccess(bid, prebidPath) ||
142+
deepAccess(bid, ortbPath) ||
143+
DEFAULT_AD_SERVER_BASE_URL;
144+
145+
return adServerBaseUrl;
146+
},
147+
148+
/**
149+
* Extract the placement ID from the given object.
150+
* @param {*} prebidBidRequestOrOrtbBidRequest
151+
* @returns string
152+
* @throws {Error} If the placement ID is not found in the given object.
153+
*/
154+
getPlacementId: function (prebidBidRequestOrOrtbBidRequest) {
155+
// Fields that would be set by the publisher in the bid configuration object in ad unit.
156+
const paramPath = 'params.placementId';
157+
const bidRequestFirstBidParam = `bids.0.${paramPath}`;
158+
159+
// ORTB path for placement ID
160+
const ortbPath = 'imp.0.tagid';
161+
162+
const placementId =
163+
deepAccess(prebidBidRequestOrOrtbBidRequest, paramPath) ||
164+
deepAccess(prebidBidRequestOrOrtbBidRequest, bidRequestFirstBidParam) ||
165+
deepAccess(prebidBidRequestOrOrtbBidRequest, ortbPath);
166+
167+
if (!placementId) {
168+
throw new Error(
169+
'Failed to obtain placement ID from the given object. ' +
170+
`Please set it via the "${paramPath}" field in the bid configuration.\n` +
171+
'Given object:\n' +
172+
JSON.stringify({functionParam: prebidBidRequestOrOrtbBidRequest}, null, 3)
173+
);
174+
}
175+
176+
return placementId;
177+
},
178+
179+
/**
180+
* !IMPORTANT: Make sure the implementation of this function matches utils.getPublisherId in
181+
* both adapters.
182+
* Extract the publisher ID from the given object.
183+
* @param {*} prebidBidRequestOrOrtbBidRequest
184+
* @returns string
185+
* @throws {Error} If the publisher ID is not found in the given object.
186+
*/
187+
getPublisherId: function (prebidBidRequestOrOrtbBidRequest) {
188+
// Fields that would be automatically set if the publisher set it
189+
// via pbjs.setBidderConfig.
190+
const ortbPath = 'site.publisher.id';
191+
const prebidPath = `ortb2.${ortbPath}`;
192+
193+
// Fields that would be set by the publisher in the bid
194+
// configuration object in ad unit.
195+
const paramPath = 'params.publisherId';
196+
const bidRequestFirstBidParam = `bids.0.${paramPath}`;
197+
198+
const publisherId =
199+
deepAccess(prebidBidRequestOrOrtbBidRequest, paramPath) ||
200+
deepAccess(prebidBidRequestOrOrtbBidRequest, bidRequestFirstBidParam) ||
201+
deepAccess(prebidBidRequestOrOrtbBidRequest, prebidPath) ||
202+
deepAccess(prebidBidRequestOrOrtbBidRequest, ortbPath);
203+
204+
if (!publisherId) {
205+
throw new Error(
206+
'Failed to obtain publisher ID from the given object. ' +
207+
`Please set it via the "${prebidPath}" field with pbjs.setBidderConfig.\n` +
208+
'Given object:\n' +
209+
JSON.stringify({functionParam: prebidBidRequestOrOrtbBidRequest}, null, 3)
210+
);
211+
}
212+
213+
return publisherId;
214+
},
215+
216+
/**
217+
* !IMPORTANT: Make sure the implementation of this function matches utils.getOrtbId in
218+
* mobkoiAnalyticsAdapter.js.
219+
* We use the bidderRequestId as the ortbId. We could do so because we only
220+
* make one ORTB request per Prebid Bidder Request.
221+
* The ID field named differently when the value passed on to different contexts.
222+
* @param {*} bid Prebid Bidder Request Object or Prebid Bid Response/Request
223+
* or ORTB Request/Response Object
224+
* @returns {string} The ORTB ID
225+
* @throws {Error} If the ORTB ID cannot be found in the given object.
226+
*/
227+
getOrtbId(bid) {
228+
const ortbId =
229+
// called bidderRequestId in Prebid Request
230+
bid.bidderRequestId ||
231+
// called seatBidId in Prebid Bid Response Object
232+
bid.seatBidId ||
233+
// called ortbId in Interpreted Prebid Response Object
234+
bid.ortbId ||
235+
// called id in ORTB object
236+
(Object.hasOwn(bid, 'imp') && bid.id);
237+
238+
if (!ortbId) {
239+
throw new Error('Unable to find the ORTB ID in the bid object. Given Object:\n' +
240+
JSON.stringify(bid, null, 2)
241+
);
242+
}
243+
244+
return ortbId;
245+
},
246+
247+
/**
248+
* Append custom fields to the prebid bid response. so that they can be accessed
249+
* in various event handlers.
250+
* @param {*} prebidBidResponse
251+
* @param {*} ortbBidResponse
252+
*/
253+
addCustomFieldsToPrebidBidResponse(prebidBidResponse, ortbBidResponse) {
254+
prebidBidResponse.ortbBidResponse = ortbBidResponse;
255+
prebidBidResponse.ortbId = ortbBidResponse.id;
256+
},
257+
258+
replaceAllMacrosInPlace(ortbBidResponse, context) {
259+
const macros = {
260+
// ORTB macros
261+
AUCTION_PRICE: ortbBidResponse.price,
262+
AUCTION_IMP_ID: ortbBidResponse.impid,
263+
AUCTION_CURRENCY: ortbBidResponse.cur,
264+
AUCTION_BID_ID: context.bidderRequest.auctionId,
265+
266+
// Custom macros
267+
BIDDING_API_BASE_URL: utils.getAdServerEndpointBaseUrl(context.bidderRequest),
268+
CREATIVE_ID: ortbBidResponse.crid,
269+
CAMPAIGN_ID: ortbBidResponse.cid,
270+
ORTB_ID: ortbBidResponse.id,
271+
PUBLISHER_ID: utils.getPublisherId(context.bidderRequest),
272+
};
273+
274+
_each(ORTB_RESPONSE_FIELDS_SUPPORT_MACROS, ortbField => {
275+
deepSetValue(
276+
ortbBidResponse,
277+
ortbField,
278+
replaceMacros(deepAccess(ortbBidResponse, ortbField), macros)
279+
);
280+
});
281+
},
282+
}

modules/mobkoiBidAdapter.md

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Overview
2+
3+
Module Name: Mobkoi Bidder Adapter
4+
Module Type: Bidder Adapter
5+
Maintainer: platformteam@mobkoi.com
6+
7+
# Description
8+
9+
Module that connects to Mobkoi Ad Server
10+
11+
### Supported formats:
12+
- Banner
13+
14+
# Test Parameters
15+
```js
16+
const adUnits = [
17+
{
18+
code: 'banner-ad',
19+
mediaTypes: {
20+
banner: { sizes: [300, 200] },
21+
},
22+
bids: [
23+
{
24+
bidder: 'mobkoi',
25+
params: {
26+
publisherId: 'module-test-publisher-id',
27+
placementId: 'moudle-test-placement-id',
28+
adServerBaseUrl: 'https://not.an.adserver.endpoint.com',
29+
}
30+
},
31+
],
32+
},
33+
];
34+
35+
pbjs.que.push(function () {
36+
pbjs.addAdUnits(adUnits);
37+
});
38+
```
39+
40+
41+
# Serve Prebid.js Locally
42+
43+
To serve Prebid.js locally with specific modules, you can use the following command:
44+
45+
```sh
46+
gulp serve-fast --modules=consentManagementTcf,tcfControl,mobkoiBidAdapter
47+
```
48+
49+
# Run bid adapter test locally
50+
51+
```sh
52+
gulp test --file=test/spec/modules/mobkoiBidAdapter_spec.js
53+
```

0 commit comments

Comments
 (0)