Skip to content

Commit 3937eaa

Browse files
authored
Merge branch 'prebid:master' into master
2 parents 6f37ca9 + fcd64ea commit 3937eaa

14 files changed

Lines changed: 1058 additions & 45 deletions

libraries/adtelligentUtils/adtelligentUtils.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { deepAccess, isArray } from '../../src/utils.js';
22
import { config } from '../../src/config.js';
33
import { BANNER, VIDEO } from '../../src/mediaTypes.js';
4+
import { getPlacementPositionUtils } from "../placementPositionInfo/placementPositionInfo.js";
45

56
export const supportedMediaTypes = [VIDEO, BANNER]
67

@@ -50,9 +51,11 @@ export function getUserSyncsFn (syncOptions, serverResponses, syncsCache = {}) {
5051
}
5152

5253
export function createTag(bidRequests, adapterRequest) {
54+
const placementEnv = getPlacementPositionUtils().getPlacementEnv()
5355
const tag = {
5456
// TODO: is 'page' the right value here?
5557
Domain: deepAccess(adapterRequest, 'refererInfo.page'),
58+
...placementEnv
5659
};
5760

5861
if (config.getConfig('coppa') === true) {
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import { deepAccess, deepSetValue, logInfo } from '../../src/utils.js';
2+
import { Renderer } from '../../src/Renderer.js';
3+
import { INSTREAM, OUTSTREAM } from '../../src/video.js';
4+
import { BANNER, MediaType, NATIVE, VIDEO } from '../../src/mediaTypes.js';
5+
import { BidResponse, VideoBidResponse } from '../../src/bidfactory.js';
6+
import { BidRequest, ORTBImp, ORTBResponse } from '../../src/prebid.public.js';
7+
import { AdapterResponse, ServerResponse } from '../../src/adapters/bidderFactory.js';
8+
9+
const OUTSTREAM_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js';
10+
11+
export function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) {
12+
if (typeof serverResponses === 'object' &&
13+
serverResponses != null &&
14+
serverResponses.length > 0 &&
15+
serverResponses[0].hasOwnProperty('body') &&
16+
serverResponses[0].body.hasOwnProperty('ext') &&
17+
serverResponses[0].body.ext.hasOwnProperty('cookies') &&
18+
typeof serverResponses[0].body.ext.cookies === 'object' &&
19+
Array.isArray(serverResponses[0].body.ext.cookies)) {
20+
return serverResponses[0].body.ext.cookies.slice(0, 5);
21+
} else {
22+
return [];
23+
}
24+
};
25+
26+
const createOustreamRendererFunction = (
27+
adUnitCode: string,
28+
width: number,
29+
height: number
30+
) => (bidResponse: VideoBidResponse) => {
31+
bidResponse.renderer.push(() => {
32+
(window as any).ANOutstreamVideo.renderAd({
33+
sizes: [width, height],
34+
targetId: adUnitCode,
35+
adResponse: bidResponse.vastXml,
36+
rendererOptions: {
37+
showBigPlayButton: false,
38+
showProgressBar: 'bar',
39+
showVolume: false,
40+
allowFullscreen: true,
41+
skippable: false,
42+
content: bidResponse.vastXml
43+
}
44+
});
45+
});
46+
};
47+
48+
export type CreateRenderPayload = {
49+
requestId: string,
50+
vastXml: string,
51+
adUnitCode: string,
52+
width: number,
53+
height: number
54+
}
55+
56+
export const createRenderer = (
57+
{ requestId, vastXml, adUnitCode, width, height }: CreateRenderPayload
58+
): Renderer | undefined => {
59+
if (!vastXml) {
60+
logInfo('No VAST in bidResponse');
61+
return;
62+
}
63+
const installPayload = {
64+
id: requestId,
65+
url: OUTSTREAM_RENDERER_URL,
66+
loaded: false,
67+
adUnitCode: adUnitCode,
68+
targetId: adUnitCode,
69+
};
70+
const renderer = Renderer.install(installPayload);
71+
renderer.setRender(createOustreamRendererFunction(adUnitCode, width, height));
72+
return renderer;
73+
};
74+
75+
export const enrichImp = (imp:ORTBImp, bidRequest:BidRequest<string>): ORTBImp => {
76+
deepSetValue(imp, 'tagid', bidRequest.adUnitCode);
77+
deepSetValue(imp, 'ext.adUnitCode', bidRequest.adUnitCode);
78+
if (imp.video) {
79+
const playerSize = deepAccess(bidRequest, 'mediaTypes.video.playerSize');
80+
const videoContext = deepAccess(bidRequest, 'mediaTypes.video.context');
81+
deepSetValue(imp, 'video.ext.playerSize', playerSize);
82+
deepSetValue(imp, 'video.ext.context', videoContext);
83+
}
84+
return imp;
85+
}
86+
87+
export function createResponse(bid:any, ortbResponse:any): BidResponse {
88+
let mediaType: MediaType = BANNER;
89+
if ([INSTREAM, OUTSTREAM].includes(bid.ext.mediaType as string)) mediaType = VIDEO;
90+
if (bid.ext.mediaType === NATIVE) mediaType = NATIVE;
91+
const response:any = {
92+
requestId: bid.impid,
93+
cpm: bid.price,
94+
width: bid.w,
95+
height: bid.h,
96+
creativeId: bid.crid,
97+
currency: ortbResponse.cur,
98+
netRevenue: true,
99+
ttl: 120,
100+
mediaType,
101+
meta: {
102+
advertiserDomains: bid.adomain,
103+
demandSource: bid.ext.ssp,
104+
},
105+
};
106+
if (bid.dealid) response.dealid = bid.dealid;
107+
108+
if (bid.ext.mediaType === BANNER) response.ad = bid.adm;
109+
if ([INSTREAM, OUTSTREAM].includes(bid.ext.mediaType as string)) response.vastXml = bid.adm;
110+
if (bid.ext.mediaType === OUTSTREAM && (bid.ext.adUnitCode)) {
111+
const renderer = createRenderer({
112+
requestId: response.requestId,
113+
vastXml: response.vastXml,
114+
adUnitCode: bid.ext.adUnitCode,
115+
width: response.width,
116+
height: response.height
117+
});
118+
if (renderer) {
119+
response.renderer = renderer;
120+
response.adUnitCode = bid.ext.adUnitCode;
121+
} else {
122+
logInfo('Could not create renderer for outstream bid');
123+
}
124+
};
125+
126+
if (bid.ext.mediaType === NATIVE) {
127+
try {
128+
response.native = { ortb: JSON.parse(bid.adm) }
129+
} catch (e) {}
130+
}
131+
return response as BidResponse;
132+
}
133+
134+
export const interpretResponse = (serverResponse: ServerResponse): AdapterResponse => {
135+
if (!serverResponse.body) return [];
136+
const respBody = serverResponse.body as ORTBResponse;
137+
if (!respBody.seatbid || respBody.seatbid.length === 0) return [];
138+
139+
const responses: BidResponse[] = [];
140+
for (let i = 0; i < respBody.seatbid.length; i++) {
141+
const seatbid = respBody.seatbid[i];
142+
for (let j = 0; j < seatbid.bid.length; j++) {
143+
const bid = seatbid.bid[j];
144+
const response:BidResponse = createResponse(bid, respBody);
145+
responses.push(response);
146+
}
147+
}
148+
return responses;
149+
}

libraries/mspa/activityControls.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import {
33
ACTIVITY_ENRICH_EIDS,
44
ACTIVITY_ENRICH_UFPD,
55
ACTIVITY_SYNC_USER,
6-
ACTIVITY_TRANSMIT_PRECISE_GEO
6+
ACTIVITY_TRANSMIT_PRECISE_GEO,
7+
ACTIVITY_TRANSMIT_UFPD
78
} from '../../src/activities/activities.js';
89
import { gppDataHandler } from '../../src/adapterManager.js';
910
import { logInfo } from '../../src/utils.js';
@@ -105,7 +106,8 @@ export function isTransmitGeoConsentDenied(cd) {
105106
const CONSENT_RULES = {
106107
[ACTIVITY_SYNC_USER]: isConsentDenied,
107108
[ACTIVITY_ENRICH_EIDS]: isConsentDenied,
108-
[ACTIVITY_ENRICH_UFPD]: isTransmitUfpdConsentDenied,
109+
[ACTIVITY_ENRICH_UFPD]: isConsentDenied,
110+
[ACTIVITY_TRANSMIT_UFPD]: isTransmitUfpdConsentDenied,
109111
[ACTIVITY_TRANSMIT_PRECISE_GEO]: isTransmitGeoConsentDenied
110112
};
111113

modules/51DegreesRtdProvider.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ export const convert51DegreesDataToOrtb2 = (data51) => {
220220
* @param {string} [device.hardwarevendor] Hardware vendor
221221
* @param {string} [device.hardwaremodel] Hardware model
222222
* @param {string[]} [device.hardwarename] Hardware name
223+
* @param {string} [device.hardwarenameprefix] Hardware name prefix (e.g. "iPhone" from "iPhone 12 Pro Max")
224+
* @param {string} [device.hardwarenameversion] Hardware name version (e.g. "12 Pro Max" from "iPhone 12 Pro Max")
223225
* @param {string} [device.platformname] Platform name
224226
* @param {string} [device.platformversion] Platform version
225227
* @param {number} [device.screenpixelsheight] Screen height in pixels
@@ -240,6 +242,7 @@ export const convert51DegreesDeviceToOrtb2 = (device) => {
240242
}
241243

242244
const deviceModel =
245+
device.hardwarenameprefix ||
243246
device.hardwaremodel || (
244247
device.hardwarename && device.hardwarename.length
245248
? device.hardwarename.join(',')
@@ -257,6 +260,7 @@ export const convert51DegreesDeviceToOrtb2 = (device) => {
257260
deepSetNotEmptyValue(ortb2Device, 'devicetype', ORTB_DEVICE_TYPE_MAP.get(device.devicetype));
258261
deepSetNotEmptyValue(ortb2Device, 'make', device.hardwarevendor);
259262
deepSetNotEmptyValue(ortb2Device, 'model', deviceModel);
263+
deepSetNotEmptyValue(ortb2Device, 'hwv', device.hardwarenameversion);
260264
deepSetNotEmptyValue(ortb2Device, 'os', device.platformname);
261265
deepSetNotEmptyValue(ortb2Device, 'osv', device.platformversion);
262266
deepSetNotEmptyValue(ortb2Device, 'h', device.screenpixelsphysicalheight || device.screenpixelsheight);

modules/51DegreesRtdProvider.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010

1111
51Degrees module enriches an OpenRTB request with [51Degrees Device Data](https://51degrees.com/documentation/index.html).
1212

13-
51Degrees module sets the following fields of the device object: `devicetype`, `make`, `model`, `os`, `osv`, `h`, `w`, `ppi`, `pxratio`. Interested bidder adapters may use these fields as needed.
13+
51Degrees module sets the following fields of the device object: `devicetype`, `make`, `model`, `hwv`, `os`, `osv`, `h`, `w`, `ppi`, `pxratio`. Interested bidder adapters may use these fields as needed.
1414

1515
The module also adds a `device.ext.fod` extension object (fod == fifty one degrees) and sets `device.ext.fod.deviceId` to a permanent device ID, which can be rapidly looked up in on-premise data, exposing over 250 properties, including device age, chipset, codec support, price, operating system and app/browser versions, age, and embedded features.
1616

1717
It also sets `device.ext.fod.tpc` key to a binary value to indicate whether third-party cookies are enabled in the browser (1 if enabled, 0 if disabled).
1818

1919
The module supports on-premise and cloud device detection services, with free options for both.
2020

21-
A free resource key for use with 51Degrees cloud service can be obtained from [51Degrees cloud configuration](https://configure.51degrees.com/7bL8jDGz). This is the simplest approach to trial the module.
21+
A free resource key for use with 51Degrees cloud service can be obtained from [51Degrees cloud configuration](https://configure.51degrees.com/jJqVnTJR). This is the simplest approach to trial the module.
2222

2323
An interface-compatible self-hosted service can be used with .NET, Java, Node, PHP, and Python. See [51Degrees examples](https://51degrees.com/documentation/_examples__device_detection__getting_started__web__on_premise.html).
2424

@@ -40,12 +40,14 @@ gulp build --modules=rtdModule,51DegreesRtdProvider,appnexusBidAdapter,...
4040

4141
#### Resource Key
4242

43-
In order to use the module, please first obtain a Resource Key using the [Configurator tool](https://configure.51degrees.com/7bL8jDGz) - choose the following properties:
43+
In order to use the module, please first obtain a Resource Key using the [Configurator tool](https://configure.51degrees.com/jJqVnTJR) - choose the following properties:
4444

4545
* DeviceId
4646
* DeviceType
4747
* HardwareVendor
4848
* HardwareName
49+
* HardwareNamePrefix
50+
* HardwareNameVersion
4951
* HardwareModel
5052
* PlatformName
5153
* PlatformVersion
@@ -111,7 +113,7 @@ pbjs.setConfig({
111113
waitForIt: true, // should be true, otherwise the auctionDelay will be ignored
112114
params: {
113115
resourceKey: '<YOUR_RESOURCE_KEY>',
114-
// Get your resource key from https://configure.51degrees.com/7bL8jDGz
116+
// Get your resource key from https://configure.51degrees.com/jJqVnTJR
115117
// alternatively, you can use the on-premise version of the 51Degrees service and connect to your chosen endpoint
116118
// onPremiseJSUrl: 'https://localhost/51Degrees.core.js'
117119
},

modules/adtelligentBidAdapter.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
isBidRequestValid,
1010
supportedMediaTypes
1111
} from '../libraries/adtelligentUtils/adtelligentUtils.js';
12-
12+
import { getPlacementPositionUtils } from "../libraries/placementPositionInfo/placementPositionInfo.js";
1313
/**
1414
* @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid
1515
* @typedef {import('../src/adapters/bidderFactory.js').BidderRequest} BidderRequest
@@ -169,11 +169,13 @@ function prepareBidRequests(bidReq) {
169169
const mediaType = deepAccess(bidReq, 'mediaTypes.video') ? VIDEO : DISPLAY;
170170
const sizes = mediaType === VIDEO ? deepAccess(bidReq, 'mediaTypes.video.playerSize') : deepAccess(bidReq, 'mediaTypes.banner.sizes');
171171
const gpid = deepAccess(bidReq, 'ortb2Imp.ext.gpid');
172+
const placementInfo = getPlacementPositionUtils().getPlacementInfo(bidReq)
172173
const bidReqParams = {
173174
'CallbackId': bidReq.bidId,
174175
'Aid': bidReq.params.aid,
175176
'AdType': mediaType,
176-
'Sizes': parseSizesInput(sizes).join(',')
177+
'Sizes': parseSizesInput(sizes).join(','),
178+
...placementInfo
177179
};
178180

179181
bidReqParams.PlacementId = bidReq.adUnitCode;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Overview
2+
3+
```
4+
Module Name: Alliance Gravity Bid Adapter
5+
Module Type: Bidder Adapter
6+
Maintainer: produit@alliancegravity.com
7+
```
8+
9+
# Description
10+
11+
Sends bids to Alliance Gravity network
12+
13+
Alliance Gravity bid adapter supports Banner, Video, Audio and Native formats
14+
15+
# Test Parameters
16+
```javascript
17+
var adUnits = [
18+
{
19+
code: 'banner-div',
20+
mediaTypes: {
21+
banner: {
22+
sizes: [[300, 250], [300,600]]
23+
}
24+
},
25+
bids: [{
26+
bidder: 'alliance_gravity',
27+
params: {
28+
srid: "test-id"
29+
}
30+
}]
31+
},
32+
];
33+
```
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { deepSetValue } from '../src/utils.js';
2+
import { AdapterRequest, BidderSpec, registerBidder } from '../src/adapters/bidderFactory.js';
3+
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
4+
import { ortbConverter } from '../libraries/ortbConverter/converter.js'
5+
6+
import { interpretResponse, enrichImp, getUserSyncs } from '../libraries/alliance_gravityUtils/index.js';
7+
import { getBoundingClientRect } from '../libraries/boundingClientRect/boundingClientRect.js';
8+
import { BidRequest, ClientBidderRequest } from '../src/adapterManager.js';
9+
import { ORTBImp, ORTBRequest } from '../src/prebid.public.js';
10+
11+
const BIDDER_CODE = 'alliance_gravity';
12+
const REQUEST_URL = 'https://pbs.production.agrvt.com/openrtb2/auction';
13+
const GVLID = 501;
14+
15+
const DEFAULT_GZIP_ENABLED = false;
16+
17+
declare module '../src/adUnits' {
18+
interface BidderParams {
19+
srid: string
20+
}
21+
}
22+
23+
const converter = ortbConverter({
24+
context: {
25+
netRevenue: true, // or false if your adapter should set bidResponse.netRevenue = false
26+
ttl: 90, // default bidResponse.ttl (when not specified in ORTB response.seatbid[].bid[].exp)
27+
},
28+
imp(buildImp, bidRequest: BidRequest<typeof BIDDER_CODE>, context) {
29+
let imp:ORTBImp = buildImp(bidRequest, context);
30+
imp = enrichImp(imp, bidRequest);
31+
const adUnitCode = bidRequest.adUnitCode;
32+
const slotEl:HTMLElement | null = document.getElementById(adUnitCode);
33+
if (slotEl) {
34+
const { width, height } = getBoundingClientRect(slotEl);
35+
deepSetValue(imp, 'ext.dimensions.slotW', width);
36+
deepSetValue(imp, 'ext.dimensions.slotH', height);
37+
deepSetValue(imp, 'ext.dimensions.cssMaxW', slotEl.style?.maxWidth);
38+
deepSetValue(imp, 'ext.dimensions.cssMaxH', slotEl.style?.maxHeight);
39+
}
40+
deepSetValue(imp, 'ext.prebid.storedrequest.id', bidRequest.params.srid);
41+
return imp;
42+
},
43+
request(buildRequest, imps, bidderRequest, context) {
44+
return buildRequest(imps, bidderRequest, context);
45+
},
46+
});
47+
48+
const isBidRequestValid = (bid:BidRequest<typeof BIDDER_CODE>): boolean => {
49+
if (!bid.params.srid || typeof bid.params.srid !== 'string' || bid.params.srid === '') {
50+
return false;
51+
}
52+
return true;
53+
};
54+
55+
const buildRequests = (
56+
bidRequests: BidRequest<typeof BIDDER_CODE>[],
57+
bidderRequest: ClientBidderRequest<typeof BIDDER_CODE>,
58+
): AdapterRequest => {
59+
const data:ORTBRequest = converter.toORTB({ bidRequests, bidderRequest })
60+
const adapterRequest:AdapterRequest = {
61+
method: 'POST',
62+
url: REQUEST_URL,
63+
data,
64+
options: {
65+
endpointCompression: DEFAULT_GZIP_ENABLED
66+
},
67+
}
68+
return adapterRequest;
69+
}
70+
71+
export const spec:BidderSpec<typeof BIDDER_CODE> = {
72+
code: BIDDER_CODE,
73+
gvlid: GVLID,
74+
supportedMediaTypes: [BANNER, VIDEO, NATIVE],
75+
isBidRequestValid,
76+
buildRequests,
77+
interpretResponse,
78+
getUserSyncs,
79+
};
80+
81+
registerBidder(spec);

0 commit comments

Comments
 (0)