+
+
diff --git a/integrationExamples/gpt/x-domain/creative.html b/integrationExamples/gpt/x-domain/creative.html
index 63842b00882..f523c87b326 100644
--- a/integrationExamples/gpt/x-domain/creative.html
+++ b/integrationExamples/gpt/x-domain/creative.html
@@ -6,8 +6,8 @@
diff --git a/libraries/creative-renderer-native/renderer.js b/libraries/creative-renderer-native/renderer.js
index d7d85cdd7ba..5651cc3f0ca 100644
--- a/libraries/creative-renderer-native/renderer.js
+++ b/libraries/creative-renderer-native/renderer.js
@@ -1,2 +1,2 @@
// this file is autogenerated, see creative/README.md
-export const RENDERER = "(()=>{\"use strict\";const e=\"Prebid Native\",t={title:\"text\",data:\"value\",img:\"url\",video:\"vasttag\"};function n(e,t){return new Promise(((n,r)=>{const i=t.createElement(\"script\");i.onload=n,i.onerror=r,i.src=e,t.body.appendChild(i)}))}function r(e,t,r,i,o=n){const{rendererUrl:s,assets:a,ortb:d,adTemplate:c}=t,l=i.document;return s?o(s,l).then((()=>{if(\"function\"!=typeof i.renderAd)throw new Error(`Renderer from '${s}' does not define renderAd()`);const e=a||[];return e.ortb=d,i.renderAd(e)})):Promise.resolve(r(c??l.body.innerHTML))}window.render=function({adId:n,native:i},{sendMessage:o},s,a=r){const{head:d,body:c}=s.document,l=()=>o(e,{action:\"resizeNativeHeight\",height:c.offsetHeight,width:c.offsetWidth}),u=function(e,{assets:n=[],ortb:r,nativeKeys:i={}}){const o=Object.fromEntries(n.map((({key:e,value:t})=>[e,t])));let s=Object.fromEntries(Object.entries(i).flatMap((([t,n])=>{const r=o.hasOwnProperty(t)?o[t]:void 0;return[[`##${n}##`,r],[`${n}:${e}`,r]]})));return r&&Object.assign(s,{\"##hb_native_linkurl##\":r.link?.url,\"##hb_native_privacy##\":r.privacy},Object.fromEntries((r.assets||[]).flatMap((e=>{const n=Object.keys(t).find((t=>e[t]));return[n&&[`##hb_native_asset_id_${e.id}##`,e[n][t[n]]],e.link?.url&&[`##hb_native_asset_link_id_${e.id}##`,e.link.url]].filter((e=>e))})))),s=Object.entries(s).concat([[/##hb_native_asset_(link_)?id_\\d+##/g]]),function(e){return s.reduce(((e,[t,n])=>e.replaceAll(t,n||\"\")),e)}}(n,i);return d&&(d.innerHTML=u(d.innerHTML)),a(n,i,u,s).then((t=>{c.innerHTML=t,\"function\"==typeof s.postRenderAd&&s.postRenderAd({adId:n,...i}),s.document.querySelectorAll(\".pb-click\").forEach((t=>{const n=t.getAttribute(\"hb_native_asset_id\");t.addEventListener(\"click\",(()=>o(e,{action:\"click\",assetId:n})))})),o(e,{action:\"fireNativeImpressionTrackers\"}),\"complete\"===s.document.readyState?l():s.onload=l}))}})();"
\ No newline at end of file
+export const RENDERER = "(()=>{\"use strict\";const e=\"Prebid Native\",t={title:\"text\",data:\"value\",img:\"url\",video:\"vasttag\"};function n(e,t){return new Promise(((n,r)=>{const i=t.createElement(\"script\");i.onload=n,i.onerror=r,i.src=e,t.body.appendChild(i)}))}function r(e){return Array.from(e.querySelectorAll('iframe[srcdoc*=\"render\"]'))}function i(e){const t=e.cloneNode(!0);return r(t).forEach((e=>e.parentNode.removeChild(e))),t.innerHTML}function o(e,t,r,o,s=n){const{rendererUrl:c,assets:d,ortb:a,adTemplate:l}=t,u=o.document;return c?s(c,u).then((()=>{if(\"function\"!=typeof o.renderAd)throw new Error(`Renderer from '${c}' does not define renderAd()`);const e=d||[];return e.ortb=a,o.renderAd(e)})):Promise.resolve(r(l??i(u.body)))}window.render=function({adId:n,native:s},{sendMessage:c},d,a=o){const{head:l,body:u}=d.document,f=()=>{u.style.display=\"none\",u.style.display=\"block\",c(e,{action:\"resizeNativeHeight\",height:u.offsetHeight,width:u.offsetWidth})};function b(e,t){const n=r(e);Array.from(e.childNodes).filter((e=>!n.includes(e))).forEach((t=>e.removeChild(t))),e.insertAdjacentHTML(\"afterbegin\",t)}const h=function(e,{assets:n=[],ortb:r,nativeKeys:i={}}){const o=Object.fromEntries(n.map((({key:e,value:t})=>[e,t])));let s=Object.fromEntries(Object.entries(i).flatMap((([t,n])=>{const r=o.hasOwnProperty(t)?o[t]:void 0;return[[`##${n}##`,r],[`${n}:${e}`,r]]})));return r&&Object.assign(s,{\"##hb_native_linkurl##\":r.link?.url,\"##hb_native_privacy##\":r.privacy},Object.fromEntries((r.assets||[]).flatMap((e=>{const n=Object.keys(t).find((t=>e[t]));return[n&&[`##hb_native_asset_id_${e.id}##`,e[n][t[n]]],e.link?.url&&[`##hb_native_asset_link_id_${e.id}##`,e.link.url]].filter((e=>e))})))),s=Object.entries(s).concat([[/##hb_native_asset_(link_)?id_\\d+##/g]]),function(e){return s.reduce(((e,[t,n])=>e.replaceAll(t,n||\"\")),e)}}(n,s);return b(l,h(i(l))),a(n,s,h,d).then((t=>{b(u,t),\"function\"==typeof d.postRenderAd&&d.postRenderAd({adId:n,...s}),d.document.querySelectorAll(\".pb-click\").forEach((t=>{const n=t.getAttribute(\"hb_native_asset_id\");t.addEventListener(\"click\",(()=>c(e,{action:\"click\",assetId:n})))})),c(e,{action:\"fireNativeImpressionTrackers\"}),\"complete\"===d.document.readyState?f():d.onload=f}))}})();"
\ No newline at end of file
diff --git a/libraries/intentIqConstants/intentIqConstants.js b/libraries/intentIqConstants/intentIqConstants.js
index ed9856fc213..2cc9acc1844 100644
--- a/libraries/intentIqConstants/intentIqConstants.js
+++ b/libraries/intentIqConstants/intentIqConstants.js
@@ -7,4 +7,4 @@ export const OPT_OUT = 'O';
export const BLACK_LIST = 'L';
export const CLIENT_HINTS_KEY = '_iiq_ch';
export const EMPTY = 'EMPTY'
-export const VERSION = 0.24
+export const VERSION = 0.25
diff --git a/libraries/liveIntentId/shared.js b/libraries/liveIntentId/shared.js
index 509f91e44d9..ab00417ccc3 100644
--- a/libraries/liveIntentId/shared.js
+++ b/libraries/liveIntentId/shared.js
@@ -59,11 +59,8 @@ export function composeIdObject(value) {
// old versions stored lipbid in unifiedId. Ensure that we can still read the data.
const lipbid = value.nonId || value.unifiedId
- if (lipbid) {
- const lipb = { ...value, lipbid };
- delete lipb.unifiedId;
- result.lipb = lipb;
- }
+ result.lipb = lipbid ? { ...value, lipbid } : value
+ delete result.lipb?.unifiedId
// Lift usage of uid2 by exposing uid2 if we were asked to resolve it.
// As adapters are applied in lexicographical order, we will always
@@ -76,6 +73,10 @@ export function composeIdObject(value) {
result.bidswitch = { 'id': value.bidswitch, ext: { provider: LI_PROVIDER_DOMAIN } }
}
+ if (value.triplelift) {
+ result.triplelift = { 'id': value.triplelift, ext: { provider: LI_PROVIDER_DOMAIN } }
+ }
+
if (value.medianet) {
result.medianet = { 'id': value.medianet, ext: { provider: LI_PROVIDER_DOMAIN } }
}
@@ -261,6 +262,18 @@ export const eids = {
}
}
},
+ 'triplelift': {
+ source: 'liveintent.triplelift.com',
+ atype: 3,
+ getValue: function(data) {
+ return data.id;
+ },
+ getUidExt: function(data) {
+ if (data.ext) {
+ return data.ext;
+ }
+ }
+ },
'vidazoo': {
source: 'liveintent.vidazoo.com',
atype: 3,
diff --git a/libraries/mspa/activityControls.js b/libraries/mspa/activityControls.js
index eb68259d585..c93748f73c7 100644
--- a/libraries/mspa/activityControls.js
+++ b/libraries/mspa/activityControls.js
@@ -24,6 +24,8 @@ export function isBasicConsentDenied(cd) {
cd.PersonalDataConsents === 2 ||
// minors 13+ who have not given consent
cd.KnownChildSensitiveDataConsents[0] === 1 ||
+ // minors 16+ who have not given consent (added in usnat version 2)
+ cd.KnownChildSensitiveDataConsents[2] === 1 ||
// minors under 13 cannot consent
isApplicable(cd.KnownChildSensitiveDataConsents[1]) ||
// covered cannot be zero
@@ -53,14 +55,31 @@ export function isConsentDenied(cd) {
}
export const isTransmitUfpdConsentDenied = (() => {
- // deny anything that smells like: genetic, biometric, state/national ID, financial, union membership,
- // or personal communication data
- const cannotBeInScope = [6, 7, 9, 10, 12].map(el => --el);
- // require consent for everything else (except geo, which is treated separately)
- const allExceptGeo = Array.from(Array(12).keys()).filter((el) => el !== SENSITIVE_DATA_GEO)
- const mustHaveConsent = allExceptGeo.filter(el => !cannotBeInScope.includes(el));
+ const sensitiveFlags = (() => {
+ // deny anything that smells like: genetic, biometric, state/national ID, financial, union membership,
+ // personal communication data, status as victim of crime (version 2), status as transgender/nonbinary (version 2)
+ const cannotBeInScope = [6, 7, 9, 10, 12, 14, 16].map(el => --el);
+ // require consent for everything else (except geo, which is treated separately)
+ const allExceptGeo = Array.from(Array(16).keys()).filter((el) => el !== SENSITIVE_DATA_GEO)
+ const mustHaveConsent = allExceptGeo.filter(el => !cannotBeInScope.includes(el));
+
+ return Object.fromEntries(
+ Object.entries({
+ 1: 12,
+ 2: 16
+ }).map(([version, cardinality]) => {
+ const isInVersion = (el) => el < cardinality
+ return [version, {
+ cannotBeInScope: cannotBeInScope.filter(isInVersion),
+ allExceptGeo: allExceptGeo.filter(isInVersion),
+ mustHaveConsent: mustHaveConsent.filter(isInVersion)
+ }]
+ })
+ )
+ })()
return function (cd) {
+ const {cannotBeInScope, mustHaveConsent, allExceptGeo} = sensitiveFlags[cd.Version];
return isConsentDenied(cd) ||
// no notice about sensitive data was given
sensitiveNoticeIs(cd, 2) ||
@@ -97,7 +116,7 @@ export function mspaRule(sids, getConsent, denies, applicableSids = () => gppDat
if (consent == null) {
return {allow: false, reason: 'consent data not available'};
}
- if (consent.Version !== 1) {
+ if (![1, 2].includes(consent.Version)) {
return {allow: false, reason: `unsupported consent specification version "${consent.Version}"`}
}
if (denies(consent)) {
diff --git a/libraries/ortbConverter/README.md b/libraries/ortbConverter/README.md
index 751971eebdc..92843c0241e 100644
--- a/libraries/ortbConverter/README.md
+++ b/libraries/ortbConverter/README.md
@@ -1,7 +1,7 @@
# Prebid.js - ORTB conversion library
-This library provides methods to convert Prebid.js bid request objects to ORTB requests,
-and ORTB responses to Prebid.js bid response objects.
+This library provides methods to convert Prebid.js bid request objects to ORTB requests,
+and ORTB responses to Prebid.js bid response objects.
## Usage
@@ -37,13 +37,25 @@ registerBidder({
})
```
-Without any customization, the library will generate complete ORTB requests, but ignores your [bid params](#params).
+Without any customization, the library will generate complete ORTB requests, but ignores your [bid params](#params).
If your endpoint sets `response.seatbid[].bid[].mtype` (part of the ORTB 2.6 spec), it will also parse the response into complete bidResponse objects. See [setting response mediaTypes](#response-mediaTypes) if that is not the case.
### Module-specific conversions
Prebid.js features that require a module also require it for their corresponding ORTB conversion logic. For example, `imp.bidfloor` is only populated if the `priceFloors` module is active; `request.cur` needs the `currency` module, and so on. Notably, this means that to get those fields populated from your unit tests, you must import those modules first; see [this suite](https://github.com/prebid/Prebid.js/blob/master/test/spec/modules/openxOrtbBidAdapter_spec.js) for an example.
+#### priceFloors extensions
+
+In addition to `imp.bidfloor` and `imp.bidfloorcur`, the `priceFloors` module also populates media type and `format` objects, if their floors differ:
+
+| Path | `getFloor` invocation |
+|-----------------------------------------------------|----------------------------------------------------------------|
+| `imp.bidfloor` & `.bidfloorcur` | `.getFloor()` |
+| `imp.banner.ext.bidfloor` & `.bidfloorcur` | `.getFloor({mediaType: 'banner', size: '*'})` |
+| `imp.banner.format[].ext.bidfloor` & `.bidfloorcur` | `.getFloor({mediaType: 'banner', size: [format.w, format.h]})` |
+| `imp.native.ext.bidfloor` & `.bidfloorcur` | `.getFloor({mediaType: 'native', size: '*'})` |
+| `imp.video.ext.bidfloor` & `.bidfloorcur` | `.getFloor({mediaType: 'video', size: '*'})` |
+
## Customization
### Modifying return values directly
@@ -57,35 +69,35 @@ deepSetValue(data.imp[0], 'ext.myCustomParam', bidRequests[0].params.myCustomPar
However, there are two restrictions (to avoid them, use the [other customization options](#fine-customization)):
- - you may not change the `imp[].id` returned by `toORTB`; they ared used internally to match responses to their requests.
- ```javascript
- const data = converter.toORTB({bidRequests, bidderRequest});
- data.imp[0].id = 'custom-imp-id' // do not do this - it will cause an error later in `fromORTB`
- ```
- See also [overriding `imp.id`](#imp-id).
- - the `request` argument passed to `fromORTB` must be the same object returned by `toORTB`.
+- you may not change the `imp[].id` returned by `toORTB`; they ared used internally to match responses to their requests.
```javascript
- let data = converter.toORTB({bidRequests, bidderRequest});
-
- data = mergeDeep( // the original object is lost
- {ext: {myCustomParam: bidRequests[0].params.myCustomParam}}, // `fromORTB` will later throw an error
- data
- );
-
- // do this instead:
- mergeDeep(
- data,
- {ext: {myCustomParam: bidRequests[0].params.myCustomParam}},
- data
- )
+ const data = converter.toORTB({bidRequests, bidderRequest});
+ data.imp[0].id = 'custom-imp-id' // do not do this - it will cause an error later in `fromORTB`
```
+ See also [overriding `imp.id`](#imp-id).
+- the `request` argument passed to `fromORTB` must be the same object returned by `toORTB`.
+ ```javascript
+ let data = converter.toORTB({bidRequests, bidderRequest});
+
+ data = mergeDeep( // the original object is lost
+ {ext: {myCustomParam: bidRequests[0].params.myCustomParam}}, // `fromORTB` will later throw an error
+ data
+ );
+
+ // do this instead:
+ mergeDeep(
+ data,
+ {ext: {myCustomParam: bidRequests[0].params.myCustomParam}},
+ data
+ )
+ ```
### Fine grained customization - imp, request, bidResponse, response
When invoked, `toORTB({bidRequests, bidderRequest})` first loops through each request in `bidRequests`, converting them into ORTB `imp` objects.
It then packages them into a single ORTB request, adding other parameters that are not imp-specific (such as for example `request.tmax`).
-Likewise, `fromORTB({request, response})` first loops through each `response.seatbid[].bid[]`, converting them into Prebid bidResponses; it then packages them into
+Likewise, `fromORTB({request, response})` first loops through each `response.seatbid[].bid[]`, converting them into Prebid bidResponses; it then packages them into
a single return value.
You can customize each of these steps using the `ortbConverter` arguments `imp`, `request`, `bidResponse` and `response`:
@@ -98,8 +110,8 @@ The arguments are:
- `buildImp`: a function taking `(bidRequest, context)` and returning an ORTB `imp` object;
- `bidRequest`: the bid request object to convert;
- `context`: a [context object](#context) that contains at least:
- - `bidderRequest`: the `bidderRequest` argument passed to `toORTB`.
-
+ - `bidderRequest`: the `bidderRequest` argument passed to `toORTB`.
+
#### Example: attaching custom bid params
```javascript
@@ -194,7 +206,7 @@ const converter = ortbConverter({
})
```
-If you know that a particular ORTB request/response pair deals with exclusively one mediaType, you may also pass it directly in the [context parameter](#context).
+If you know that a particular ORTB request/response pair deals with exclusively one mediaType, you may also pass it directly in the [context parameter](#context).
Note that - compared to the above - this has additional effects, because `context.mediaType` is also considered during `imp` generation - see [special context properties](#special-context).
```javascript
@@ -223,7 +235,7 @@ const converter = ortbConverter({
### Customizing the response: `response(buildResponse, bidResponses, ortbResponse, context)`
-Invoked once, after all `seatbid[].bid[]` objects have been converted to corresponding bid responses. The value returned
+Invoked once, after all `seatbid[].bid[]` objects have been converted to corresponding bid responses. The value returned
by this function is also the value returned by `fromORTB`.
The arguments are:
@@ -249,7 +261,7 @@ const converter = ortbConverter({
### Even finer grained customization - processor overrides
Each of the four conversion steps described above - imp, request, bidResponse and response - is further broken down into
-smaller units of work (called _processors_). For example, when the currency module is included, it adds a _request processor_
+smaller units of work (called _processors_). For example, when the currency module is included, it adds a _request processor_
that sets `request.cur`; the priceFloors module adds an _imp processor_ that sets `imp.bidfloor` and `imp.bidfloorcur`, and so on.
Each processor can be overridden or disabled through the `overrides` argument:
@@ -310,21 +322,21 @@ const converter = ortbConverter({
Processor overrides are similar to the override options described above, except that they take the object to process as argument:
- `imp` processor overrides take `(orig, imp, bidRequest, context)`, where:
- - `orig` is the processor function being overridden, which itself takes `(imp, bidRequest, context)`;
- - `imp` is the (partial) imp object to modify;
- - `bidRequest` and `context` are the same arguments passed to [imp](#imp).
+ - `orig` is the processor function being overridden, which itself takes `(imp, bidRequest, context)`;
+ - `imp` is the (partial) imp object to modify;
+ - `bidRequest` and `context` are the same arguments passed to [imp](#imp).
- `request` processor overrides take `(orig, ortbRequest, bidderRequest, context)`, where:
- - `orig` is the processor function being overridden, and takes `(ortbRequest, bidderRequest, context)`;
- - `ortbRequest` is the partial request to modify;
- - `bidderRequest` and `context` are the same arguments passed to [request](#reuqest).
-- `bidResponse` processor overrides take `(orig, bidResponse, bid, context)`, where:
- - `orig` is the processor function being overridden, and takes `(bidResponse, bid, context)`;
- - `bidResponse` is the partial bid response to modify;
- - `bid` and `context` are the same arguments passed to [bidResponse](#bidResponse)
+ - `orig` is the processor function being overridden, and takes `(ortbRequest, bidderRequest, context)`;
+ - `ortbRequest` is the partial request to modify;
+ - `bidderRequest` and `context` are the same arguments passed to [request](#reuqest).
+- `bidResponse` processor overrides take `(orig, bidResponse, bid, context)`, where:
+ - `orig` is the processor function being overridden, and takes `(bidResponse, bid, context)`;
+ - `bidResponse` is the partial bid response to modify;
+ - `bid` and `context` are the same arguments passed to [bidResponse](#bidResponse)
- `response` processor overrides take `(orig, response, ortbResponse, context)`, where:
- - `orig` is the processor function being overriden, and takes `(response, ortbResponse, context)`;
- - `response` is the partial response to modify;
- - `ortbRespones` and `context` are the same arguments passed to [response](#response).
+ - `orig` is the processor function being overriden, and takes `(response, ortbResponse, context)`;
+ - `response` is the partial response to modify;
+ - `ortbRespones` and `context` are the same arguments passed to [response](#response).
### The `context` argument
@@ -354,19 +366,19 @@ const converter = ortbConverter({
For ease of use, the conversion logic gives special meaning to some context properties:
- - `currency`: a currency string (e.g. `'EUR'`). If specified, overrides the currency to use for computing price floors and `request.cur`. If omitted, both default to `getConfig('currency.adServerCurrency')`.
- - `mediaType`: a bid mediaType (`'banner'`, `'video'`, or `'native'`). If specified:
+- `currency`: a currency string (e.g. `'EUR'`). If specified, overrides the currency to use for computing price floors and `request.cur`. If omitted, both default to `getConfig('currency.adServerCurrency')`.
+- `mediaType`: a bid mediaType (`'banner'`, `'video'`, or `'native'`). If specified:
- disables `imp` generation for other media types (i.e., if `context.mediaType === 'banner'`, only `imp.banner` will be populated; `imp.video` and `imp.native` will not, even if the bid request specifies them);
- is passed as the `mediaType` option to `bidRequest.getFloor` when computing price floors;
- sets `bidResponse.mediaType`.
- - `nativeRequest`: a plain object that serves as the base value for `imp.native.request` (and is relevant only for native bid requests).
- If not specified, the only property that is guaranteed to be populated is `assets`, since Prebid does not require anything else to define a native adUnit. You can use `context.nativeRequest` to provide other properties; for example, you may want to signal support for native impression trackers by setting it to `{eventtrackers: [{event: 1, methods: [1, 2]}]}` (see also the [ORTB Native spec](https://www.iab.com/wp-content/uploads/2018/03/OpenRTB-Native-Ads-Specification-Final-1.2.pdf)).
- - `netRevenue`: the value to set as `bidResponse.netRevenue`. This is a required property of bid responses that does not have a clear ORTB counterpart.
- - `ttl`: the default value to use for `bidResponse.ttl` (if the ORTB response does not provide one in `seatbid[].bid[].exp`).
-
+- `nativeRequest`: a plain object that serves as the base value for `imp.native.request` (and is relevant only for native bid requests).
+ If not specified, the only property that is guaranteed to be populated is `assets`, since Prebid does not require anything else to define a native adUnit. You can use `context.nativeRequest` to provide other properties; for example, you may want to signal support for native impression trackers by setting it to `{eventtrackers: [{event: 1, methods: [1, 2]}]}` (see also the [ORTB Native spec](https://www.iab.com/wp-content/uploads/2018/03/OpenRTB-Native-Ads-Specification-Final-1.2.pdf)).
+- `netRevenue`: the value to set as `bidResponse.netRevenue`. This is a required property of bid responses that does not have a clear ORTB counterpart.
+- `ttl`: the default value to use for `bidResponse.ttl` (if the ORTB response does not provide one in `seatbid[].bid[].exp`).
+
## Prebid Server extensions
-If your endpoint is a Prebid Server instance, you may take advantage of the `pbsExtension` companion library, which adds a number of processors that can populate and parse PBS-specific extensions (typically prefixed `ext.prebid`); these include bidder params (with `transformBidParams`), bidder aliases, targeting keys, and others.
+If your endpoint is a Prebid Server instance, you may take advantage of the `pbsExtension` companion library, which adds a number of processors that can populate and parse PBS-specific extensions (typically prefixed `ext.prebid`); these include bidder params (with `transformBidParams`), bidder aliases, targeting keys, and others.
```javascript
import {pbsExtensions} from '../../libraries/pbsExtensions/pbsExtensions.js'
diff --git a/libraries/riseUtils/constants.js b/libraries/riseUtils/constants.js
new file mode 100644
index 00000000000..4acb9920291
--- /dev/null
+++ b/libraries/riseUtils/constants.js
@@ -0,0 +1,20 @@
+import {BANNER, NATIVE, VIDEO} from '../../src/mediaTypes.js';
+
+const OW_GVLID = 280
+export const SUPPORTED_AD_TYPES = [BANNER, VIDEO, NATIVE];
+export const ADAPTER_VERSION = '7.0.0';
+export const DEFAULT_TTL = 360;
+export const DEFAULT_CURRENCY = 'USD';
+export const BASE_URL = 'https://hb.yellowblue.io/';
+export const BIDDER_CODE = 'rise';
+export const DEFAULT_GVLID = 1043;
+
+export const ALIASES = [
+ { code: 'risexchange', gvlid: DEFAULT_GVLID },
+ { code: 'openwebxchange', gvlid: OW_GVLID }
+]
+
+export const MODES = {
+ PRODUCTION: 'hb-multi',
+ TEST: 'hb-multi-test'
+};
diff --git a/libraries/riseUtils/index.js b/libraries/riseUtils/index.js
index 60f31ef2603..3046e6dcf4a 100644
--- a/libraries/riseUtils/index.js
+++ b/libraries/riseUtils/index.js
@@ -1,34 +1,135 @@
import {
- isArray,
- isFn,
+ contains,
deepAccess,
+ getBidIdParameter,
+ isArray,
isEmpty,
- contains,
+ isFn,
isInteger,
- getBidIdParameter,
- isPlainObject
+ isPlainObject,
+ logInfo,
+ triggerPixel
} from '../../src/utils.js';
-import { BANNER, VIDEO } from '../../src/mediaTypes.js';
+import {BANNER, NATIVE, VIDEO} from '../../src/mediaTypes.js';
import {config} from '../../src/config.js';
+import {ADAPTER_VERSION, DEFAULT_CURRENCY, DEFAULT_TTL, SUPPORTED_AD_TYPES} from './constants.js';
+
+export const makeBaseSpec = (baseUrl, modes) => {
+ return {
+ version: ADAPTER_VERSION,
+ supportedMediaTypes: SUPPORTED_AD_TYPES,
+ buildRequests: function (validBidRequests, bidderRequest) {
+ const combinedRequestsObject = {};
+
+ // use data from the first bid, to create the general params for all bids
+ const generalObject = validBidRequests[0];
+ const testMode = generalObject.params.testMode;
+ const rtbDomain = generalObject.params.rtbDomain || baseUrl;
+
+ combinedRequestsObject.params = generateGeneralParams(generalObject, bidderRequest);
+ combinedRequestsObject.bids = generateBidsParams(validBidRequests, bidderRequest);
+
+ return {
+ method: 'POST',
+ url: getEndpoint(testMode, rtbDomain, modes),
+ data: combinedRequestsObject
+ }
+ },
+ interpretResponse: function ({ body }) {
+ const bidResponses = [];
+
+ if (body.bids) {
+ body.bids.forEach(adUnit => {
+ const bidResponse = buildBidResponse(adUnit);
+ bidResponses.push(bidResponse);
+ });
+ }
+
+ return bidResponses;
+ },
+ getUserSyncs: function (syncOptions, serverResponses) {
+ const syncs = [];
+ for (const response of serverResponses) {
+ if (syncOptions.iframeEnabled && deepAccess(response, 'body.params.userSyncURL')) {
+ syncs.push({
+ type: 'iframe',
+ url: deepAccess(response, 'body.params.userSyncURL')
+ });
+ }
+ if (syncOptions.pixelEnabled && isArray(deepAccess(response, 'body.params.userSyncPixels'))) {
+ const pixels = response.body.params.userSyncPixels.map(pixel => {
+ return {
+ type: 'image',
+ url: pixel
+ }
+ });
+ syncs.push(...pixels);
+ }
+ }
+ return syncs;
+ },
+ onBidWon: function (bid) {
+ if (bid == null) {
+ return;
+ }
+
+ logInfo('onBidWon:', bid);
+ if (bid.hasOwnProperty('nurl') && bid.nurl.length > 0) {
+ triggerPixel(bid.nurl);
+ }
+ }
+ }
+}
+
+export function getBidRequestMediaTypes(bidRequest) {
+ const mediaTypes = deepAccess(bidRequest, 'mediaTypes');
+ if (isPlainObject(mediaTypes)) {
+ return Object.keys(mediaTypes);
+ }
+ return [];
+}
+
+export function getPos(bidRequest) {
+ const mediaTypes = getBidRequestMediaTypes(bidRequest);
+ const firstMediaType = mediaTypes[0];
+ if (mediaTypes.length === 1) {
+ return deepAccess(bidRequest, `mediaTypes.${firstMediaType}.pos`);
+ }
+}
+
+export function getName(bidRequest) {
+ const mediaTypes = getBidRequestMediaTypes(bidRequest);
+ const firstMediaType = mediaTypes[0];
+ if (mediaTypes.length === 1) {
+ return deepAccess(bidRequest, `mediaTypes.${firstMediaType}.name`);
+ }
+}
-export function getFloor(bid, mediaType) {
+export function getFloor(bid) {
if (!isFn(bid.getFloor)) {
return 0;
}
+
+ const mediaTypes = getBidRequestMediaTypes(bid)
+ const firstMediaType = mediaTypes[0];
+
let floorResult = bid.getFloor({
currency: 'USD',
- mediaType: mediaType,
+ mediaType: mediaTypes.length === 1 ? firstMediaType : '*',
size: '*'
});
return isPlainObject(floorResult) && floorResult.currency === 'USD' && floorResult.floor ? floorResult.floor : 0;
}
-export function getSizesArray(bid, mediaType) {
+export function getSizesArray(bid) {
let sizesArray = [];
- if (deepAccess(bid, `mediaTypes.${mediaType}.sizes`)) {
- sizesArray = bid.mediaTypes[mediaType].sizes;
- } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0) {
+ const mediaTypes = getBidRequestMediaTypes(bid);
+ const firstMediaType = mediaTypes[0];
+
+ if (mediaTypes.length === 1 && deepAccess(bid, `mediaTypes.${firstMediaType}.sizes`)) {
+ sizesArray = bid.mediaTypes[firstMediaType].sizes;
+ } else if (isArray(bid.sizes) && bid.sizes.length > 0) {
sizesArray = bid.sizes;
}
@@ -111,18 +212,17 @@ export function generateBidsParams(validBidRequests, bidderRequest) {
export function generateBidParameters(bid, bidderRequest) {
const { params } = bid;
- const mediaType = isBanner(bid) ? BANNER : VIDEO;
- const sizesArray = getSizesArray(bid, mediaType);
+ const mediaTypes = getBidRequestMediaTypes(bid);
if (isNaN(params.floorPrice)) {
params.floorPrice = 0;
}
const bidObject = {
- mediaType,
+ mediaType: mediaTypes.join(','),
adUnitCode: getBidIdParameter('adUnitCode', bid),
- sizes: sizesArray,
- floorPrice: Math.max(getFloor(bid, mediaType), params.floorPrice),
+ sizes: getSizesArray(bid),
+ floorPrice: Math.max(getFloor(bid), params.floorPrice),
bidId: getBidIdParameter('bidId', bid),
loop: bid.bidderRequestsCount || 0,
bidderRequestId: getBidIdParameter('bidderRequestId', bid),
@@ -130,8 +230,8 @@ export function generateBidParameters(bid, bidderRequest) {
coppa: 0,
};
- const pos = deepAccess(bid, `mediaTypes.${mediaType}.pos`);
- if (pos) {
+ const pos = getPos(bid);
+ if (isInteger(pos)) {
bidObject.pos = pos;
}
@@ -140,21 +240,11 @@ export function generateBidParameters(bid, bidderRequest) {
bidObject.gpid = gpid;
}
- const placementId = params.placementId || deepAccess(bid, `mediaTypes.${mediaType}.name`);
+ const placementId = params.placementId || getName(bid);
if (placementId) {
bidObject.placementId = placementId;
}
- const mimes = deepAccess(bid, `mediaTypes.${mediaType}.mimes`);
- if (mimes) {
- bidObject.mimes = mimes;
- }
-
- const api = deepAccess(bid, `mediaTypes.${mediaType}.api`);
- if (api) {
- bidObject.api = api;
- }
-
const sua = deepAccess(bid, `ortb2.device.sua`);
if (sua) {
bidObject.sua = sua;
@@ -165,11 +255,11 @@ export function generateBidParameters(bid, bidderRequest) {
bidObject.coppa = 1;
}
- if (mediaType === VIDEO) {
+ if (mediaTypes.includes(VIDEO)) {
const playbackMethod = deepAccess(bid, `mediaTypes.video.playbackmethod`);
let playbackMethodValue;
- if (Array.isArray(playbackMethod) && isInteger(playbackMethod[0])) {
+ if (isArray(playbackMethod) && isInteger(playbackMethod[0])) {
playbackMethodValue = playbackMethod[0];
} else if (isInteger(playbackMethod)) {
playbackMethodValue = playbackMethod;
@@ -213,19 +303,36 @@ export function generateBidParameters(bid, bidderRequest) {
if (plcmt) {
bidObject.plcmt = plcmt;
}
+
+ const mimes = deepAccess(bid, `mediaTypes.video.mimes`);
+ if (mimes) {
+ bidObject.mimes = mimes;
+ }
+
+ const api = deepAccess(bid, `mediaTypes.video.api`);
+ if (api) {
+ bidObject.api = api;
+ }
+ }
+
+ if (mediaTypes.includes(NATIVE)) {
+ const nativeOrtbRequest = deepAccess(bid, `nativeOrtbRequest`);
+ if (nativeOrtbRequest) {
+ bidObject.nativeOrtbRequest = nativeOrtbRequest;
+ }
}
return bidObject;
}
-export function buildBidResponse(adUnit, DEFAULT_CURRENCY, TTL, VIDEO, BANNER) {
+export function buildBidResponse(adUnit) {
const bidResponse = {
requestId: adUnit.requestId,
cpm: adUnit.cpm,
currency: adUnit.currency || DEFAULT_CURRENCY,
width: adUnit.width,
height: adUnit.height,
- ttl: adUnit.ttl || TTL,
+ ttl: adUnit.ttl || DEFAULT_TTL,
creativeId: adUnit.creativeId,
netRevenue: adUnit.netRevenue || true,
nurl: adUnit.nurl,
@@ -239,6 +346,8 @@ export function buildBidResponse(adUnit, DEFAULT_CURRENCY, TTL, VIDEO, BANNER) {
bidResponse.vastXml = adUnit.vastXml;
} else if (adUnit.mediaType === BANNER) {
bidResponse.ad = adUnit.ad;
+ } else if (adUnit.mediaType === NATIVE) {
+ bidResponse.native = {ortb: adUnit.native};
}
if (adUnit.adomain && adUnit.adomain.length) {
@@ -248,10 +357,6 @@ export function buildBidResponse(adUnit, DEFAULT_CURRENCY, TTL, VIDEO, BANNER) {
return bidResponse;
}
-function isBanner(bid) {
- return bid.mediaTypes && bid.mediaTypes.banner;
-}
-
export function generateGeneralParams(generalObject, bidderRequest, adapterVersion) {
const domain = window.location.hostname;
const { syncEnabled, filterSettings } = config.getConfig('userSync') || {};
diff --git a/libraries/sizeUtils/sizeUtils.js b/libraries/sizeUtils/sizeUtils.js
index 41cdd71df89..c0fe8510d7e 100644
--- a/libraries/sizeUtils/sizeUtils.js
+++ b/libraries/sizeUtils/sizeUtils.js
@@ -27,3 +27,28 @@ export function getAdUnitSizes(adUnit) {
}
return sizes;
}
+
+/**
+ * Normalize adUnit.mediaTypes.banner.sizes to Array.>
+ *
+ * @param {Array. | Array.>} bidSizes - value of adUnit.mediaTypes.banner.sizes.
+ * @returns {Array.>} - Normalized value.
+ */
+
+export function normalizeBannerSizes(bidSizes) {
+ let sizes = [];
+ if (Array.isArray(bidSizes) && bidSizes.length === 2 && !Array.isArray(bidSizes[0])) {
+ sizes.push({
+ width: parseInt(bidSizes[0], 10),
+ height: parseInt(bidSizes[1], 10),
+ });
+ } else if (Array.isArray(bidSizes) && Array.isArray(bidSizes[0])) {
+ bidSizes.forEach((size) => {
+ sizes.push({
+ width: parseInt(size[0], 10),
+ height: parseInt(size[1], 10),
+ });
+ });
+ }
+ return sizes;
+}
diff --git a/libraries/teqblazeUtils/bidderUtils.js b/libraries/teqblazeUtils/bidderUtils.js
index f9484ebe5d1..84010adbbd6 100644
--- a/libraries/teqblazeUtils/bidderUtils.js
+++ b/libraries/teqblazeUtils/bidderUtils.js
@@ -148,7 +148,11 @@ export const buildRequestsBase = (config) => {
page,
placements,
coppa: deepAccess(bidderRequest, 'ortb2.regs.coppa') ? 1 : 0,
- tmax: bidderRequest.timeout
+ tmax: bidderRequest.timeout,
+ bcat: deepAccess(bidderRequest, 'ortb2.bcat'),
+ badv: deepAccess(bidderRequest, 'ortb2.badv'),
+ bapp: deepAccess(bidderRequest, 'ortb2.bapp'),
+ battr: deepAccess(bidderRequest, 'ortb2.battr')
};
if (bidderRequest.uspConsent) {
diff --git a/modules/33acrossIdSystem.js b/modules/33acrossIdSystem.js
index fb5a7712f1f..277cb8b2f6d 100644
--- a/modules/33acrossIdSystem.js
+++ b/modules/33acrossIdSystem.js
@@ -60,7 +60,7 @@ function calculateResponseObj(response) {
};
}
-function calculateQueryStringParams({ pid, hem }, gdprConsentData, enabledStorageTypes) {
+function calculateQueryStringParams({ pid, pubProvidedHem }, gdprConsentData, enabledStorageTypes) {
const uspString = uspDataHandler.getConsentData();
const coppaValue = coppaDataHandler.getCoppa();
const gppConsent = gppDataHandler.getConsentData();
@@ -98,9 +98,9 @@ function calculateQueryStringParams({ pid, hem }, gdprConsentData, enabledStorag
params.tp = encodeURIComponent(tp);
}
- const hemParam = hem || getStoredValue(STORAGE_HEM_KEY, enabledStorageTypes);
- if (hemParam) {
- params.sha256 = encodeURIComponent(hemParam);
+ const hem = pubProvidedHem || getStoredValue(STORAGE_HEM_KEY, enabledStorageTypes);
+ if (hem) {
+ params.sha256 = encodeURIComponent(hem);
}
return params;
@@ -145,10 +145,51 @@ function getStoredValue(key, enabledStorageTypes) {
return storedValue;
}
-function handleSupplementalId(key, id, storageConfig) {
- id
- ? storeValue(key, id, storageConfig)
- : deleteFromStorage(key);
+function filterEnabledSupplementalIds({ tp, fp, hem }, { storeFpid, storeTpid, envelopeAvailable }) {
+ const ids = [];
+
+ if (storeFpid) {
+ ids.push(
+ /**
+ * [
+ * ,
+ * < ID value to store or remove >,
+ * < clear flag: indicates if existing storage item should be removed or not based on certain condition>
+ * ]
+ */
+ [STORAGE_FPID_KEY, fp, !fp],
+ [STORAGE_HEM_KEY, hem, !envelopeAvailable] // Clear hashed email if envelope is not available
+ );
+ }
+
+ if (storeTpid) {
+ ids.push([STORAGE_TPID_KEY, tp, !tp]);
+ }
+
+ return ids;
+}
+
+function updateSupplementalIdStorage(supplementalId, storageConfig) {
+ const [ key, id, clear ] = supplementalId;
+
+ if (clear) {
+ deleteFromStorage(key);
+
+ return;
+ }
+
+ if (id) {
+ storeValue(key, id, storageConfig);
+ }
+}
+
+function handleSupplementalIds(ids, { enabledStorageTypes, expires, ...options }) {
+ filterEnabledSupplementalIds(ids, options).forEach((supplementalId) => {
+ updateSupplementalIdStorage(supplementalId, {
+ enabledStorageTypes,
+ expires
+ })
+ });
}
/** @type {Submodule} */
@@ -197,8 +238,10 @@ export const thirtyThreeAcrossIdSubmodule = {
const {
storeFpid = DEFAULT_1PID_SUPPORT,
storeTpid = DEFAULT_TPID_SUPPORT, apiUrl = API_URL,
- ...options
+ pid,
+ hem
} = params;
+ const pubProvidedHem = hem || window._33across?.hem?.sha256;
return {
callback(cb) {
@@ -218,19 +261,17 @@ export const thirtyThreeAcrossIdSubmodule = {
});
}
- if (storeFpid) {
- handleSupplementalId(STORAGE_FPID_KEY, responseObj.fp, {
- enabledStorageTypes,
- expires: storageConfig.expires
- });
- }
-
- if (storeTpid) {
- handleSupplementalId(STORAGE_TPID_KEY, responseObj.tp, {
- enabledStorageTypes,
- expires: storageConfig.expires
- });
- }
+ handleSupplementalIds({
+ fp: responseObj.fp,
+ tp: responseObj.tp,
+ hem: pubProvidedHem
+ }, {
+ storeFpid,
+ storeTpid,
+ envelopeAvailable: !!responseObj.envelope,
+ enabledStorageTypes,
+ expires: storageConfig.expires
+ });
cb(responseObj.envelope);
},
@@ -239,7 +280,7 @@ export const thirtyThreeAcrossIdSubmodule = {
cb();
}
- }, calculateQueryStringParams(options, gdprConsentData, enabledStorageTypes), {
+ }, calculateQueryStringParams({ pid, pubProvidedHem }, gdprConsentData, enabledStorageTypes), {
method: 'GET',
withCredentials: true
});
diff --git a/modules/33acrossIdSystem.md b/modules/33acrossIdSystem.md
index b6b68622344..7dabb08eebd 100644
--- a/modules/33acrossIdSystem.md
+++ b/modules/33acrossIdSystem.md
@@ -57,4 +57,5 @@ The following settings are available in the `params` property in `userSync.userI
### HEM Collection
-33Across ID System supports user's hashed email, if available in storage.
+33Across ID System supports user's hashed emails (HEMs). HEMs could be collected from 3 different sources in following
+priority order: `hem` configuration parameter, global `_33across.hem.sha256` field or from storage (cookie or local storage).
diff --git a/modules/acuityadsBidAdapter.js b/modules/acuityadsBidAdapter.js
index 3caaacb46a4..b94234c2c26 100644
--- a/modules/acuityadsBidAdapter.js
+++ b/modules/acuityadsBidAdapter.js
@@ -12,7 +12,7 @@ export const spec = {
gvlid: GVLID,
supportedMediaTypes: [BANNER, VIDEO, NATIVE],
- isBidRequestValid: isBidRequestValid(['placementId']),
+ isBidRequestValid: isBidRequestValid(),
buildRequests: buildRequests(AD_URL),
interpretResponse,
getUserSyncs: getUserSyncs(SYNC_URL)
diff --git a/modules/adlooxAnalyticsAdapter.js b/modules/adlooxAnalyticsAdapter.js
index 0a953584e26..c7321799f3c 100644
--- a/modules/adlooxAnalyticsAdapter.js
+++ b/modules/adlooxAnalyticsAdapter.js
@@ -81,8 +81,6 @@ const PARAMS_DEFAULT = {
'id11': '$ADLOOX_WEBSITE'
};
-const NOOP = function() {};
-
let analyticsAdapter = Object.assign(adapter({ analyticsType: 'endpoint' }), {
track({ eventType, args }) {
if (!analyticsAdapter[`handle_${eventType}`]) return;
@@ -109,6 +107,10 @@ analyticsAdapter.enableAnalytics = function(config) {
logError(MODULE, 'invalid js options value');
return;
}
+ if (isStr(config.options.js) && !/\.adlooxtracking\.(com|ru)$/.test(parseUrl(config.options.js, { 'noDecodeWholeURL': true }).host)) {
+ logError(MODULE, "invalid js options value, must be a sub-domain of 'adlooxtracking.com'");
+ return;
+ }
if (!(config.options.toselector === undefined || isFn(config.options.toselector))) {
logError(MODULE, 'invalid toselector options value');
return;
@@ -221,20 +223,24 @@ analyticsAdapter.url = function(url, args, bid) {
return url + a2qs(args);
}
+const preloaded = {};
analyticsAdapter[`handle_${EVENTS.AUCTION_END}`] = function(auctionDetails) {
if (!(auctionDetails.auctionStatus == AUCTION_COMPLETED && auctionDetails.bidsReceived.length > 0)) return;
- analyticsAdapter[`handle_${EVENTS.AUCTION_END}`] = NOOP;
-
- logMessage(MODULE, 'preloading verification JS');
const uri = parseUrl(analyticsAdapter.url(`${analyticsAdapter.context.js}#`));
+ const href = `${uri.protocol}://${uri.host}${uri.pathname}`;
+ if (preloaded[href]) return;
+
+ logMessage(MODULE, 'preloading verification JS');
const link = document.createElement('link');
- link.setAttribute('href', `${uri.protocol}://${uri.host}${uri.pathname}`);
+ link.setAttribute('href', href);
link.setAttribute('rel', 'preload');
link.setAttribute('as', 'script');
// TODO fix rules violation
insertElement(link);
+
+ preloaded[href] = true;
}
analyticsAdapter[`handle_${EVENTS.BID_WON}`] = function(bid) {
diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js
index ffd02e43a99..f8bf37af42a 100644
--- a/modules/adnuntiusBidAdapter.js
+++ b/modules/adnuntiusBidAdapter.js
@@ -1,5 +1,5 @@
import { registerBidder } from '../src/adapters/bidderFactory.js';
-import {BANNER, VIDEO} from '../src/mediaTypes.js';
+import {BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js';
import {isStr, isEmpty, deepAccess, getUnixTimestampFromNow, convertObjectToArray, getWindowTop} from '../src/utils.js';
import { config } from '../src/config.js';
import { getStorageManager } from '../src/storageManager.js';
@@ -12,7 +12,7 @@ const BIDDER_CODE_DEAL_ALIASES = [1, 2, 3, 4, 5].map(num => {
const ENDPOINT_URL = 'https://ads.adnuntius.delivery/i';
const ENDPOINT_URL_EUROPE = 'https://europe.delivery.adnuntius.com/i';
const GVLID = 855;
-const SUPPORTED_MEDIA_TYPES = [BANNER, VIDEO];
+const SUPPORTED_MEDIA_TYPES = [BANNER, VIDEO, NATIVE];
const MAXIMUM_DEALS_LIMIT = 5;
const VALID_BID_TYPES = ['netBid', 'grossBid'];
const METADATA_KEY = 'adn.metaData';
@@ -319,6 +319,9 @@ export const spec = {
const adUnit = {...bidTargeting, auId: bid.params.auId, targetId: targetId};
if (mediaType === VIDEO) {
adUnit.adType = 'VAST';
+ } else if (mediaType === NATIVE) {
+ adUnit.adType = 'NATIVE';
+ adUnit.nativeRequest = mediaTypeData.ortb;
}
const maxDeals = Math.max(0, Math.min(bid.params.maxDeals || 0, MAXIMUM_DEALS_LIMIT));
if (maxDeals > 0) {
@@ -391,10 +394,13 @@ export const spec = {
const isDeal = dealCount > 0;
const renderSource = isDeal ? ad : adUnit;
if (renderSource.vastXml) {
- adResponse.vastXml = renderSource.vastXml
- adResponse.mediaType = VIDEO
+ adResponse.vastXml = renderSource.vastXml;
+ adResponse.mediaType = VIDEO;
+ } else if (renderSource.nativeJson) {
+ adResponse.mediaType = NATIVE;
+ adResponse.native = renderSource.nativeJson;
} else {
- adResponse.ad = renderSource.html
+ adResponse.ad = renderSource.html;
}
return adResponse;
}
diff --git a/modules/airgridRtdProvider.js b/modules/airgridRtdProvider.js
index 300744d62fe..c547528a57e 100644
--- a/modules/airgridRtdProvider.js
+++ b/modules/airgridRtdProvider.js
@@ -17,7 +17,7 @@ import {MODULE_TYPE_RTD} from '../src/activities/modules.js';
const MODULE_NAME = 'realTimeData';
const SUBMODULE_NAME = 'airgrid';
-const AG_TCF_ID = 782;
+const MIQ_TCF_ID = 101;
export const AG_AUDIENCE_IDS_KEY = 'edkt_matched_audience_ids';
export const storage = getStorageManager({
@@ -76,7 +76,7 @@ export function setAudiencesAsBidderOrtb2(bidConfig, rtdConfig, audiences) {
const agUserData = [
{
- id: String(AG_TCF_ID),
+ id: String(MIQ_TCF_ID),
ext: {
segtax: 540,
},
@@ -129,7 +129,7 @@ export const airgridSubmodule = {
name: SUBMODULE_NAME,
init: init,
getBidRequestData: passAudiencesToBidders,
- gvlid: AG_TCF_ID
+ gvlid: MIQ_TCF_ID
};
submodule(MODULE_NAME, airgridSubmodule);
diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js
index 4270b47d91e..099dd989a1d 100644
--- a/modules/appnexusBidAdapter.js
+++ b/modules/appnexusBidAdapter.js
@@ -354,18 +354,23 @@ export const spec = {
if (bidRequests[0].userId) {
let eids = [];
- bidRequests[0].userIdAsEids.forEach(eid => {
- if (!eid || !eid.uids || eid.uids.length < 1) { return; }
- eid.uids.forEach(uid => {
- let tmp = {'source': eid.source, 'id': uid.id};
- if (eid.source == 'adserver.org') {
- tmp.rti_partner = 'TDID';
- } else if (eid.source == 'uidapi.com') {
- tmp.rti_partner = 'UID2';
- }
- eids.push(tmp);
+ const processEids = (uids) => {
+ uids.forEach(eid => {
+ if (!eid || !eid.uids || eid.uids.length < 1) { return; }
+ eid.uids.forEach(uid => {
+ let tmp = {'source': eid.source, 'id': uid.id};
+ if (eid.source == 'adserver.org') {
+ tmp.rti_partner = 'TDID';
+ } else if (eid.source == 'uidapi.com') {
+ tmp.rti_partner = 'UID2';
+ }
+ eids.push(tmp);
+ });
});
- });
+ }
+ if (bidRequests[0].userIdAsEids) {
+ processEids(bidRequests[0].userIdAsEids);
+ }
if (eids.length) {
payload.eids = eids;
}
diff --git a/modules/asoBidAdapter.js b/modules/asoBidAdapter.js
index 08612757de1..388eee86fe2 100644
--- a/modules/asoBidAdapter.js
+++ b/modules/asoBidAdapter.js
@@ -18,7 +18,8 @@ export const spec = {
aliases: [
{code: 'bcmint'},
{code: 'bidgency'},
- {code: 'kuantyx'}
+ {code: 'kuantyx'},
+ {code: 'cordless'}
],
isBidRequestValid: bid => {
diff --git a/modules/blueBidAdapter.js b/modules/blueBidAdapter.js
new file mode 100644
index 00000000000..ba8d0a9e8fe
--- /dev/null
+++ b/modules/blueBidAdapter.js
@@ -0,0 +1,122 @@
+import { ortbConverter } from '../libraries/ortbConverter/converter.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { BANNER } from '../src/mediaTypes.js';
+import { getStorageManager } from '../src/storageManager.js';
+import { deepSetValue, isFn, isPlainObject } from '../src/utils.js';
+
+const BIDDER_CODE = 'blue';
+const ENDPOINT_URL = 'https://bidder-us-east-1.getblue.io/engine/?src=prebid';
+const GVLID = 620; // GVLID for your bidder
+const COOKIE_NAME = 'ckid'; // Cookie name for identifying users
+const CURRENCY = 'USD'; // Currency used in bid floors
+
+export const storage = getStorageManager({ bidderCode: BIDDER_CODE });
+
+const converter = ortbConverter({
+ context: {
+ netRevenue: true, // Default netRevenue setting
+ ttl: 100, // Default time-to-live for bid responses
+ },
+ imp,
+ request,
+});
+
+function request(buildRequest, imps, bidderRequest, context) {
+ let request = buildRequest(imps, bidderRequest, context);
+ deepSetValue(request, 'site.publisher.id', context.publisherId);
+ return request;
+}
+
+function imp(buildImp, bidRequest, context) {
+ const imp = buildImp(bidRequest, context);
+ const floor = getBidFloor(bidRequest);
+ if (floor) {
+ imp.bidfloor = floor;
+ imp.bidfloorcur = CURRENCY;
+ }
+ return imp;
+}
+
+function getBidFloor(bid) {
+ if (isFn(bid.getFloor)) {
+ let floor = bid.getFloor({
+ currency: CURRENCY,
+ mediaType: BANNER,
+ size: '*',
+ });
+ if (
+ isPlainObject(floor) &&
+ !isNaN(floor.floor) &&
+ floor.currency === CURRENCY
+ ) {
+ return floor.floor;
+ }
+ }
+ return null;
+}
+
+export const spec = {
+ code: BIDDER_CODE,
+ gvlid: GVLID,
+ supportedMediaTypes: [BANNER], // Supported ad types
+
+ // Validate bid request
+ isBidRequestValid: function (bid) {
+ return !!bid.params.placementId && !!bid.params.publisherId;
+ },
+
+ // Build OpenRTB requests using `ortbConverter`
+ buildRequests: function (validBidRequests, bidderRequest) {
+ const context = {
+ publisherId: validBidRequests.find(
+ (bidRequest) => bidRequest.params?.publisherId
+ )?.params.publisherId,
+ };
+
+ const ortbRequest = converter.toORTB({
+ bidRequests: validBidRequests,
+ bidderRequest,
+ context,
+ });
+
+ // Add GVLID and cookie ID to the request
+ ortbRequest.ext = ortbRequest.ext || {};
+ deepSetValue(ortbRequest, 'ext.gvlid', GVLID);
+
+ // Include user cookie if available
+ const ckid = storage.getDataFromLocalStorage('blueID') || storage.getCookie(COOKIE_NAME) || null;
+ if (ckid) {
+ deepSetValue(ortbRequest, 'user.ext.buyerid', ckid);
+ }
+
+ return {
+ method: 'POST',
+ url: ENDPOINT_URL,
+ data: JSON.stringify(ortbRequest),
+ options: {
+ contentType: 'text/plain',
+ },
+ };
+ },
+
+ // Interpret OpenRTB responses using `ortbConverter`
+ interpretResponse: function (serverResponse, request) {
+ const ortbResponse = serverResponse.body;
+
+ // Parse the OpenRTB response into Prebid bid responses
+ const prebidResponses = converter.fromORTB({
+ response: ortbResponse,
+ request: request.data,
+ }).bids;
+
+ // Example: Modify bid responses if needed
+ prebidResponses.forEach((bid) => {
+ bid.meta = bid.meta || {};
+ bid.meta.adapterVersion = '1.0.0';
+ });
+
+ return prebidResponses;
+ },
+};
+
+registerBidder(spec);
diff --git a/modules/blueBidAdapter.md b/modules/blueBidAdapter.md
new file mode 100644
index 00000000000..4f446b1ff4b
--- /dev/null
+++ b/modules/blueBidAdapter.md
@@ -0,0 +1,28 @@
+# Overview
+
+Module Name: Blue Bidder Adapter
+Module Type: Bidder Adapter
+Maintainer: celsooliveira@getblue.io
+
+# Description
+
+Module that connects to Blue's demand sources.
+
+# Test Parameters
+```
+ var adUnits = [
+ {
+ code: 'banner-ad-div',
+ sizes: [[300, 250], [728, 90]],
+ bids: [
+ {
+ bidder: 'blue',
+ params: {
+ publisherId: "xpto",
+ placementId: "xpto",
+ }
+ }
+ ]
+ }
+ ];
+```
diff --git a/modules/conceptxBidAdapter.js b/modules/conceptxBidAdapter.js
index 47c50a4c0ad..67ebd88e4e4 100644
--- a/modules/conceptxBidAdapter.js
+++ b/modules/conceptxBidAdapter.js
@@ -57,6 +57,9 @@ export const spec = {
return bidResponses
}
const firstSeat = firstBid.ads[0]
+ if (!firstSeat) {
+ return bidResponses
+ }
const bidResponse = {
requestId: firstSeat.requestId,
cpm: firstSeat.cpm,
diff --git a/modules/consumableBidAdapter.js b/modules/consumableBidAdapter.js
index cb802508de9..e01078890f9 100644
--- a/modules/consumableBidAdapter.js
+++ b/modules/consumableBidAdapter.js
@@ -33,7 +33,8 @@ export const spec = {
/**
* Make a server request from the list of BidRequests.
*
- * @param {validBidRequests[]} - an array of bids
+ * @param {validBidRequests[]} validBidRequests An array of bids
+ * @param {Object} bidderRequest The bidder's request info.
* @return ServerRequest Info describing the request to the server.
*/
@@ -300,6 +301,7 @@ function retrieveAd(decision, unitId, unitName) {
function handleEids(data, validBidRequests) {
let bidUserIdAsEids = deepAccess(validBidRequests, '0.userIdAsEids');
if (isArray(bidUserIdAsEids) && bidUserIdAsEids.length > 0) {
+ bidUserIdAsEids = bidUserIdAsEids.filter(e => typeof e === 'object');
deepSetValue(data, 'user.eids', bidUserIdAsEids);
} else {
deepSetValue(data, 'user.eids', undefined);
diff --git a/modules/contxtfulBidAdapter.js b/modules/contxtfulBidAdapter.js
index 4905c499998..f7d263ae74f 100644
--- a/modules/contxtfulBidAdapter.js
+++ b/modules/contxtfulBidAdapter.js
@@ -8,7 +8,7 @@ import {
interpretResponse,
getUserSyncs as getUserSyncsLib,
} from '../libraries/teqblazeUtils/bidderUtils.js';
-import {ortbConverter} from '../libraries/ortbConverter/converter.js';
+import { ortbConverter } from '../libraries/ortbConverter/converter.js';
// Constants
const BIDDER_CODE = 'contxtful';
@@ -75,7 +75,7 @@ const extractParameters = (config) => {
// Construct the Payload towards the Bidding endpoint
const buildRequests = (validBidRequests = [], bidderRequest = {}) => {
- const ortb2 = converter.toORTB({bidderRequest: bidderRequest, bidRequests: validBidRequests});
+ const ortb2 = converter.toORTB({ bidderRequest: bidderRequest, bidRequests: validBidRequests });
const bidRequests = [];
_each(validBidRequests, bidRequest => {
@@ -88,7 +88,7 @@ const buildRequests = (validBidRequests = [], bidderRequest = {}) => {
});
const config = pbjsConfig.getConfig();
config.pbjsVersion = PREBID_VERSION;
- const {version, customer} = extractParameters(config)
+ const { version, customer } = extractParameters(config)
const adapterUrl = buildUrl({
protocol: 'https',
host: BIDDER_ENDPOINT,
@@ -151,6 +151,17 @@ const getSamplingRate = (bidderConfig, eventType) => {
return entry ? entry[1] : DEFAULT_SAMPLING_RATE;
};
+const logBidderError = ({ error, bidderRequest }) => {
+ if (error) {
+ let jsonReason = {
+ message: error.reason?.message,
+ stack: error.reason?.stack,
+ };
+ error.reason = jsonReason;
+ }
+ logEvent('onBidderError', { error, bidderRequest });
+};
+
// Handles the logging of events
const logEvent = (eventType, data) => {
try {
@@ -159,7 +170,7 @@ const logEvent = (eventType, data) => {
// Get Config
const bidderConfig = pbjsConfig.getConfig();
- const {version, customer} = extractParameters(bidderConfig);
+ const { version, customer } = extractParameters(bidderConfig);
// Sampled monitoring
if (['onBidBillable', 'onAdRenderSucceeded'].includes(eventType)) {
@@ -206,12 +217,12 @@ export const spec = {
buildRequests,
interpretResponse,
getUserSyncs,
- onBidWon: function(bid) { logEvent('onBidWon', bid); },
- onBidBillable: function(bid) { logEvent('onBidBillable', bid); },
- onAdRenderSucceeded: function(bid) { logEvent('onAdRenderSucceeded', bid); },
- onSetTargeting: function(bid) { },
- onTimeout: function(timeoutData) { logEvent('onTimeout', timeoutData); },
- onBidderError: function({ error, bidderRequest }) { logEvent('onBidderError', { error, bidderRequest }); },
+ onBidWon: function (bid) { logEvent('onBidWon', bid); },
+ onBidBillable: function (bid) { logEvent('onBidBillable', bid); },
+ onAdRenderSucceeded: function (bid) { logEvent('onAdRenderSucceeded', bid); },
+ onSetTargeting: function (bid) { },
+ onTimeout: function (timeoutData) { logEvent('onTimeout', timeoutData); },
+ onBidderError: logBidderError,
};
registerBidder(spec);
diff --git a/modules/contxtfulRtdProvider.js b/modules/contxtfulRtdProvider.js
index 55623c00591..1bd977afed1 100644
--- a/modules/contxtfulRtdProvider.js
+++ b/modules/contxtfulRtdProvider.js
@@ -15,6 +15,7 @@ import {
isEmpty,
buildUrl,
isArray,
+ generateUUID,
} from '../src/utils.js';
import { loadExternalScript } from '../src/adloader.js';
import { getStorageManager } from '../src/storageManager.js';
@@ -26,13 +27,17 @@ const MODULE = `${MODULE_NAME}RtdProvider`;
const CONTXTFUL_HOSTNAME_DEFAULT = 'api.receptivity.io';
const CONTXTFUL_DEFER_DEFAULT = 0;
+let _sm;
+function sm() {
+ return _sm ??= generateUUID();
+}
+
const storageManager = getStorageManager({
moduleType: MODULE_TYPE_RTD,
moduleName: MODULE_NAME,
});
let rxApi = null;
-let isFirstBidRequestCall = true;
/**
* Return current receptivity value for the requester.
@@ -150,7 +155,7 @@ function initCustomer(config) {
addConnectorEventListener(customer, config);
- const loadScript = () => loadExternalScript(CONNECTOR_URL, MODULE_TYPE_RTD, MODULE_NAME);
+ const loadScript = () => loadExternalScript(CONNECTOR_URL, MODULE_TYPE_RTD, MODULE_NAME, undefined, undefined, { 'data-sm': sm() });
// Optionally defer the loading of the script
if (Number.isFinite(defer) && defer > 0) {
setTimeout(loadScript, defer);
@@ -228,9 +233,6 @@ function getTargetingData(adUnits, config, _userConsent) {
*/
function getBidRequestData(reqBidsConfigObj, onDone, config, userConsent) {
function onReturn() {
- if (isFirstBidRequestCall) {
- isFirstBidRequestCall = false;
- }
onDone();
}
@@ -245,16 +247,10 @@ function getBidRequestData(reqBidsConfigObj, onDone, config, userConsent) {
let fromStorage = prepareBatch(bidders, (bidder) => loadSessionReceptivity(`${config?.params?.customer}_${bidder}`));
let sources = [fromStorage, fromApi];
- if (isFirstBidRequestCall) {
- sources.reverse();
- }
let rxBatch = Object.assign(...sources);
- let singlePointEvents;
- if (isEmpty(rxBatch)) {
- singlePointEvents = btoa(JSON.stringify({ ui: getUiEvents() }));
- }
+ let singlePointEvents = btoa(JSON.stringify({ ui: getUiEvents() }));
bidders
.forEach(bidderCode => {
@@ -266,6 +262,7 @@ function getBidRequestData(reqBidsConfigObj, onDone, config, userConsent) {
ext: {
rx: rxBatch[bidderCode],
events: singlePointEvents,
+ sm: sm(),
params: {
ev: config.params?.version,
ci: config.params?.customer,
diff --git a/modules/eplanningBidAdapter.js b/modules/eplanningBidAdapter.js
index 1fe5cb09c10..5bd1eab8863 100644
--- a/modules/eplanningBidAdapter.js
+++ b/modules/eplanningBidAdapter.js
@@ -4,6 +4,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js';
import {getStorageManager} from '../src/storageManager.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';
import {isSlotMatchingAdUnitCode} from '../libraries/gptUtils/gptUtils.js';
+import {serializeSupplyChain} from '../libraries/schainSerializer/schainSerializer.js';
const BIDDER_CODE = 'eplanning';
export const storage = getStorageManager({bidderCode: BIDDER_CODE});
@@ -39,6 +40,7 @@ export const spec = {
const method = 'GET';
const dfpClientId = '1';
const sec = 'ROS';
+ const schain = bidRequests[0].schain;
let url;
let params;
const urlConfig = getUrlConfig(bidRequests);
@@ -70,7 +72,9 @@ export const spec = {
if (pcrs) {
params.crs = pcrs;
}
-
+ if (schain && schain.nodes.length <= 2) {
+ params.sch = serializeSupplyChain(schain, ['asi', 'sid', 'hp', 'rid', 'name', 'domain']);
+ }
if (referrerUrl) {
params.fr = cutUrl(referrerUrl);
}
diff --git a/modules/escalaxBidAdapter.js b/modules/escalaxBidAdapter.js
index 70a10d748bc..027e41d7c56 100644
--- a/modules/escalaxBidAdapter.js
+++ b/modules/escalaxBidAdapter.js
@@ -84,8 +84,8 @@ export const spec = {
const subdomain = getSubdomain();
const endpointURL = ESCALAX_URL
.replace(ESCALAX_SUBDOMAIN_MACRO, subdomain || ESCALAX_DEFAULT_SUBDOMAIN)
- .replace(ESCALAX_ACCOUNT_ID_MACRO, sourceId)
- .replace(ESCALAX_SOURCE_ID_MACRO, accountId);
+ .replace(ESCALAX_SOURCE_ID_MACRO, sourceId)
+ .replace(ESCALAX_ACCOUNT_ID_MACRO, accountId);
const request = converter.toORTB({ bidRequests: validBidRequests, bidderRequest });
return {
method: 'POST',
diff --git a/modules/improvedigitalBidAdapter.js b/modules/improvedigitalBidAdapter.js
index 33ba08e8fd2..a0347382dd1 100644
--- a/modules/improvedigitalBidAdapter.js
+++ b/modules/improvedigitalBidAdapter.js
@@ -6,14 +6,6 @@ import {Renderer} from '../src/Renderer.js';
import {hasPurpose1Consent} from '../src/utils/gdpr.js';
import {ortbConverter} from '../libraries/ortbConverter/converter.js';
import {convertCurrency} from '../libraries/currencyUtils/currency.js';
-/**
- * See https://github.com/prebid/Prebid.js/pull/8827 for details on linting exception
- * ImproveDigital only imports after winning a bid and only if the creative cannot reach top
- * Also see https://github.com/prebid/Prebid.js/issues/11656
- */
-// eslint-disable-next-line no-restricted-imports
-import {loadExternalScript} from '../src/adloader.js';
-import { MODULE_TYPE_BIDDER } from '../src/activities/modules.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -225,7 +217,6 @@ export const CONVERTER = ortbConverter({
renderer: ID_OUTSTREAM.createRenderer(bidRequest)
})
}
- ID_RAZR.forwardBid({bidRequest, bid: bidResponse});
return bidResponse;
},
overrides: {
@@ -367,61 +358,3 @@ const ID_OUTSTREAM = {
bid.renderer.handleVideoEvent({ id, eventName });
},
};
-
-const ID_RAZR = {
- RENDERER_URL: 'https://cdn.360yield.com/razr/tag.js',
-
- forwardBid({bidRequest, bid}) {
- if (bid.mediaType !== BANNER) {
- return;
- }
-
- const cfg = {
- prebid: {
- bidRequest,
- bid
- }
- };
-
- const cfgStr = JSON.stringify(cfg).replace(/<\/script>/ig, '\\x3C/script>');
- const s = ``;
- // prepend RAZR config to ad markup:
- bid.ad = s + bid.ad;
-
- this.installListener();
- },
-
- installListener() {
- if (this._listenerInstalled) {
- return;
- }
-
- window.addEventListener('message', function(e) {
- const data = e.data?.razr?.load;
- if (!data) {
- return;
- }
-
- if (e.source) {
- data.source = e.source;
- if (data.id) {
- e.source.postMessage({
- razr: {
- id: data.id
- }
- }, '*');
- }
- }
-
- const ns = window.razr = window.razr || {};
- ns.q = ns.q || [];
- ns.q.push(data);
-
- if (!ns.loaded) {
- loadExternalScript(ID_RAZR.RENDERER_URL, MODULE_TYPE_BIDDER, BIDDER_CODE);
- }
- });
-
- this._listenerInstalled = true;
- }
-};
diff --git a/modules/intentIqAnalyticsAdapter.js b/modules/intentIqAnalyticsAdapter.js
index 1cf270117b7..e3b5625dc7b 100644
--- a/modules/intentIqAnalyticsAdapter.js
+++ b/modules/intentIqAnalyticsAdapter.js
@@ -304,7 +304,7 @@ function constructFullUrl(data) {
'&jsver=' + VERSION +
'&source=pbjs' +
'&payload=' + JSON.stringify(report) +
- '&uh=' + iiqAnalyticsAnalyticsAdapter.initOptions.clientsHints +
+ '&uh=' + encodeURIComponent(iiqAnalyticsAnalyticsAdapter.initOptions.clientsHints) +
(gppData.gppString ? '&gpp=' + encodeURIComponent(gppData.gppString) : '');
url = appendVrrefAndFui(url, iiqAnalyticsAnalyticsAdapter.initOptions.domainName);
diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js
index e2a712e0afc..a96b0da132b 100644
--- a/modules/ixBidAdapter.js
+++ b/modules/ixBidAdapter.js
@@ -496,6 +496,11 @@ function parseBid(rawBid, currency, bidRequest) {
if (rawBid.ext?.dsa) {
bid.meta.dsa = rawBid.ext.dsa
}
+
+ if (rawBid.ext?.ibv) {
+ bid.ext = bid.ext || {}
+ bid.ext.ibv = rawBid.ext.ibv
+ }
return bid;
}
diff --git a/modules/liveIntentRtdProvider.js b/modules/liveIntentRtdProvider.js
new file mode 100644
index 00000000000..92cd09ae346
--- /dev/null
+++ b/modules/liveIntentRtdProvider.js
@@ -0,0 +1,52 @@
+/**
+ * This module adds the LiveIntent provider to the Real Time Data module (rtdModule).
+ */
+import { submodule } from '../src/hook.js';
+import {deepAccess, deepSetValue} from '../src/utils.js'
+
+/**
+ * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule
+ * @typedef {import('../modules/rtdModule/index.js').SubmoduleConfig} SubmoduleConfig
+ * @typedef {import('../modules/rtdModule/index.js').UserConsentData} UserConsentData
+ */
+
+const SUBMODULE_NAME = 'liveintent';
+const GVLID = 148;
+
+/**
+ * Init
+ * @param {Object} config Module configuration
+ * @param {UserConsentData} userConsent User consent
+ * @returns true
+ */
+const init = (config, userConsent) => {
+ return true;
+}
+
+/**
+ * onBidRequest is called for each bidder during an auction and contains the bids for that bidder.
+ *
+ * @param {Object} bidRequest
+ * @param {SubmoduleConfig} config
+ * @param {UserConsentData} userConsent
+ */
+
+function onBidRequest(bidRequest, config, userConsent) {
+ bidRequest.bids.forEach(bid => {
+ const providedSegmentsFromUserId = deepAccess(bid, 'userId.lipb.segments', [])
+ if (providedSegmentsFromUserId.length > 0) {
+ const providedSegments = { name: 'liveintent.com', segment: providedSegmentsFromUserId.map(id => ({ id })) }
+ const existingData = deepAccess(bid, 'ortb2.user.data', [])
+ deepSetValue(bid, 'ortb2.user.data', existingData.concat(providedSegments))
+ }
+ })
+}
+
+export const liveIntentRtdSubmodule = {
+ name: SUBMODULE_NAME,
+ gvlid: GVLID,
+ init: init,
+ onBidRequestEvent: onBidRequest
+};
+
+submodule('realTimeData', liveIntentRtdSubmodule);
diff --git a/modules/liveIntentRtdProvider.md b/modules/liveIntentRtdProvider.md
new file mode 100644
index 00000000000..e742fb74bee
--- /dev/null
+++ b/modules/liveIntentRtdProvider.md
@@ -0,0 +1,45 @@
+# Overview
+
+Module Name: LiveIntent Provider
+Module Type: Rtd Provider
+Maintainer: product@liveIntent.com
+
+# Description
+
+This module extracts segments from `bidRequest.userId.lipb.segments` enriched by the userID module and
+injects them in `ortb2.user.data` array entry.
+
+Please visit [LiveIntent](https://www.liveIntent.com/) for more information.
+
+# Testing
+
+To run the example and test the Rtd provider:
+
+```sh
+gulp serve --modules=appnexusBidAdapter,rtdModule,liveIntentRtdProvider,userId,liveIntentIdSystem
+```
+
+Open chrome with this URL:
+`http://localhost:9999/integrationExamples/gpt/liveIntentRtdProviderExample.html`
+
+To run the unit test:
+```sh
+gulp test --file "test/spec/modules/liveIntentRtdProvider_spec.js"
+```
+
+# Integration
+
+```bash
+gulp build --modules=userId,liveIntentIdSystem,rtdModule,liveIntentRtdProvider
+```
+
+```javascript
+pbjs.setConfig({
+ realTimeData: {
+ dataProviders:[{
+ name: 'liveintent',
+ waitForIt: true
+ }]
+ }
+});
+```
diff --git a/modules/magniteAnalyticsAdapter.js b/modules/magniteAnalyticsAdapter.js
index 7eb20439f5f..25adf2b8a3c 100644
--- a/modules/magniteAnalyticsAdapter.js
+++ b/modules/magniteAnalyticsAdapter.js
@@ -264,7 +264,8 @@ export const parseBidResponse = (bid, previousBidResponse) => {
return pick(bid, [
'bidPriceUSD', () => responsePrice,
'dealId', dealId => dealId || undefined,
- 'mediaType',
+ 'mediaType', () => bid?.meta?.mediaType ?? bid.mediaType,
+ 'ogMediaType', () => bid?.meta?.mediaType && bid.mediaType !== bid?.meta?.mediaType ? bid.mediaType : undefined,
'dimensions', () => {
const width = bid.width || bid.playerWidth;
const height = bid.height || bid.playerHeight;
diff --git a/modules/medianetAnalyticsAdapter.js b/modules/medianetAnalyticsAdapter.js
index 1b4ccc45c88..526399014c1 100644
--- a/modules/medianetAnalyticsAdapter.js
+++ b/modules/medianetAnalyticsAdapter.js
@@ -20,6 +20,8 @@ import {AUCTION_COMPLETED, AUCTION_IN_PROGRESS, getPriceGranularity} from '../sr
import {includes} from '../src/polyfill.js';
import {getGlobal} from '../src/prebidGlobal.js';
import {convertCurrency} from '../libraries/currencyUtils/currency.js';
+import {INSTREAM, OUTSTREAM} from '../src/video.js';
+import {ADPOD} from '../src/mediaTypes.js';
const analyticsType = 'endpoint';
const ENDPOINT = 'https://pb-logs.media.net/log?logid=kfk&evtid=prebid_analytics_events_client';
@@ -266,11 +268,23 @@ class AdSlot {
tmax: this.tmax,
targ: JSON.stringify(this.targeting),
ismn: this.medianetPresent,
- vplcmtt: this.context,
+ vplcmtt: this.getVideoPlacement(),
},
this.adext && {'adext': JSON.stringify(this.adext)},
);
}
+ getVideoPlacement() {
+ switch (this.context) {
+ case INSTREAM:
+ return 1
+ case OUTSTREAM:
+ return 6
+ case ADPOD:
+ return 7
+ default:
+ return 0
+ }
+ }
}
class BidWrapper {
diff --git a/modules/minutemediaBidAdapter.js b/modules/minutemediaBidAdapter.js
index 4e83c5c6db4..d5aae8a84d0 100644
--- a/modules/minutemediaBidAdapter.js
+++ b/modules/minutemediaBidAdapter.js
@@ -1,35 +1,19 @@
-import {
- logWarn,
- logInfo,
- isArray,
- deepAccess,
- triggerPixel,
-} from '../src/utils.js';
-import { registerBidder } from '../src/adapters/bidderFactory.js';
-import { BANNER, VIDEO } from '../src/mediaTypes.js';
-import {
- getEndpoint,
- generateBidsParams,
- generateGeneralParams,
- buildBidResponse,
-} from '../libraries/riseUtils/index.js';
+import {logWarn} from '../src/utils.js';
+import {registerBidder} from '../src/adapters/bidderFactory.js';
+import {makeBaseSpec} from '../libraries/riseUtils/index.js';
-const SUPPORTED_AD_TYPES = [BANNER, VIDEO];
const BIDDER_CODE = 'minutemedia';
-const ADAPTER_VERSION = '6.0.0';
-const TTL = 360;
-const DEFAULT_CURRENCY = 'USD';
const BASE_URL = 'https://hb.minutemedia-prebid.com/';
+const GVLID = 918;
const MODES = {
PRODUCTION: 'hb-mm-multi',
TEST: 'hb-multi-mm-test'
};
export const spec = {
+ ...makeBaseSpec(BASE_URL, MODES),
code: BIDDER_CODE,
- gvlid: 918,
- version: ADAPTER_VERSION,
- supportedMediaTypes: SUPPORTED_AD_TYPES,
+ gvlid: GVLID,
isBidRequestValid: function (bidRequest) {
if (!bidRequest.params) {
logWarn('no params have been set to MinuteMedia adapter');
@@ -42,64 +26,6 @@ export const spec = {
}
return true;
- },
- buildRequests: function (validBidRequests, bidderRequest) {
- const combinedRequestsObject = {};
-
- const generalObject = validBidRequests[0];
- const testMode = generalObject.params.testMode;
-
- combinedRequestsObject.params = generateGeneralParams(generalObject, bidderRequest, ADAPTER_VERSION);
- combinedRequestsObject.bids = generateBidsParams(validBidRequests, bidderRequest);
-
- return {
- method: 'POST',
- url: getEndpoint(testMode, BASE_URL, MODES),
- data: combinedRequestsObject
- };
- },
- interpretResponse: function ({ body }) {
- const bidResponses = [];
-
- if (body.bids) {
- body.bids.forEach(adUnit => {
- const bidResponse = buildBidResponse(adUnit, DEFAULT_CURRENCY, TTL, VIDEO, BANNER);
- bidResponses.push(bidResponse);
- });
- }
-
- return bidResponses;
- },
- getUserSyncs: function (syncOptions, serverResponses) {
- const syncs = [];
- for (const response of serverResponses) {
- if (syncOptions.iframeEnabled && deepAccess(response, 'body.params.userSyncURL')) {
- syncs.push({
- type: 'iframe',
- url: deepAccess(response, 'body.params.userSyncURL')
- });
- }
- if (syncOptions.pixelEnabled && isArray(deepAccess(response, 'body.params.userSyncPixels'))) {
- const pixels = response.body.params.userSyncPixels.map(pixel => {
- return {
- type: 'image',
- url: pixel
- };
- });
- syncs.push(...pixels);
- }
- }
- return syncs;
- },
- onBidWon: function (bid) {
- if (bid == null) {
- return;
- }
-
- logInfo('onBidWon:', bid);
- if (bid.hasOwnProperty('nurl') && bid.nurl.length > 0) {
- triggerPixel(bid.nurl);
- }
}
};
diff --git a/modules/minutemediaBidAdapter.md b/modules/minutemediaBidAdapter.md
index 66b54adaf0e..b22cf856364 100644
--- a/modules/minutemediaBidAdapter.md
+++ b/modules/minutemediaBidAdapter.md
@@ -13,7 +13,7 @@ Module that connects to MinuteMedia's demand sources.
The MinuteMedia adapter requires setup and approval from the MinuteMedia. Please reach out to hb@minutemedia.com to create an MinuteMedia account.
-The adapter supports Video(instream) & Banner.
+The adapter supports Video(instream), Banner, Native and multi-format bid requests.
# Bid Parameters
## Video
diff --git a/modules/missenaBidAdapter.js b/modules/missenaBidAdapter.js
index cd5650e73c2..c5c8678e0b1 100644
--- a/modules/missenaBidAdapter.js
+++ b/modules/missenaBidAdapter.js
@@ -12,6 +12,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js';
import { getStorageManager } from '../src/storageManager.js';
import { getCurrencyFromBidderRequest } from '../libraries/ortb2Utils/currency.js';
import { isAutoplayEnabled } from '../libraries/autoplayDetection/autoplay.js';
+import { normalizeBannerSizes } from '../libraries/sizeUtils/sizeUtils.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -87,6 +88,7 @@ function toPayload(bidRequest, bidderRequest) {
payload.coppa = bidderRequest?.ortb2?.regs?.coppa ? 1 : 0;
payload.autoplay = isAutoplayEnabled() === true ? 1 : 0;
payload.screen = { height: screen.height, width: screen.width };
+ payload.sizes = normalizeBannerSizes(bidRequest.mediaTypes.banner.sizes);
return {
method: 'POST',
diff --git a/modules/nativeRendering.js b/modules/nativeRendering.js
index 8e6b6baab55..a6a404a0253 100644
--- a/modules/nativeRendering.js
+++ b/modules/nativeRendering.js
@@ -7,7 +7,8 @@ import {getCreativeRendererSource} from '../src/creativeRenderers.js';
function getRenderingDataHook(next, bidResponse, options) {
if (isNativeResponse(bidResponse)) {
next.bail({
- native: getNativeRenderingData(bidResponse, auctionManager.index.getAdUnit(bidResponse))
+ native: getNativeRenderingData(bidResponse, auctionManager.index.getAdUnit(bidResponse)),
+ rendererVersion: 2 // 9.28 fixed a rendering bug; this signals to PUC that the native renderer is safe to use
})
} else {
next(bidResponse, options)
diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js
index ecf512f11d3..d4e5fea1cf0 100644
--- a/modules/oguryBidAdapter.js
+++ b/modules/oguryBidAdapter.js
@@ -1,10 +1,11 @@
'use strict';
-import {BANNER} from '../src/mediaTypes.js';
-import {getWindowSelf, getWindowTop, isFn, logWarn, deepAccess, isPlainObject} from '../src/utils.js';
-import {registerBidder} from '../src/adapters/bidderFactory.js';
-import {ajax} from '../src/ajax.js';
-import {getAdUnitSizes} from '../libraries/sizeUtils/sizeUtils.js';
+import { BANNER } from '../src/mediaTypes.js';
+import { getWindowSelf, getWindowTop, isFn, deepAccess, isPlainObject, deepSetValue, mergeDeep } from '../src/utils.js';
+import { registerBidder } from '../src/adapters/bidderFactory.js';
+import { ajax } from '../src/ajax.js';
+import { getAdUnitSizes } from '../libraries/sizeUtils/sizeUtils.js';
+import { ortbConverter } from '../libraries/ortbConverter/converter.js';
const BIDDER_CODE = 'ogury';
const GVLID = 31;
@@ -12,38 +13,68 @@ const DEFAULT_TIMEOUT = 1000;
const BID_HOST = 'https://mweb-hb.presage.io/api/header-bidding-request';
const TIMEOUT_MONITORING_HOST = 'https://ms-ads-monitoring-events.presage.io';
const MS_COOKIE_SYNC_DOMAIN = 'https://ms-cookie-sync.presage.io';
-const ADAPTER_VERSION = '1.7.0';
+const ADAPTER_VERSION = '2.0.0';
+
+export const converter = ortbConverter({
+ context: {
+ netRevenue: true,
+ ttl: 60,
+ mediaType: 'banner'
+ },
+
+ request(buildRequest, imps, bidderRequest, context) {
+ const req = buildRequest(imps, bidderRequest, context);
+ req.tmax = DEFAULT_TIMEOUT;
+ deepSetValue(req, 'device.pxratio', window.devicePixelRatio);
+ deepSetValue(req, 'site.page', getWindowContext().location.href);
+
+ req.ext = mergeDeep({}, req.ext, {
+ adapterversion: ADAPTER_VERSION,
+ prebidversion: '$prebid.version$'
+ });
-function getClientWidth() {
- const documentElementClientWidth = window.top.document.documentElement.clientWidth
- ? window.top.document.documentElement.clientWidth
- : 0
- const innerWidth = window.top.innerWidth ? window.top.innerWidth : 0
- const outerWidth = window.top.outerWidth ? window.top.outerWidth : 0
- const screenWidth = window.top.screen.width ? window.top.screen.width : 0
+ const bidWithAssetKey = bidderRequest.bids.find(bid => Boolean(deepAccess(bid, 'params.assetKey', false)));
+ if (bidWithAssetKey) deepSetValue(req, 'site.id', bidWithAssetKey.params.assetKey);
- return documentElementClientWidth || innerWidth || outerWidth || screenWidth
-}
+ const bidWithUserIds = bidderRequest.bids.find(bid => Boolean(bid.userId));
+ if (bidWithUserIds) deepSetValue(req, 'user.ext.uids', bidWithUserIds.userId);
-function getClientHeight() {
- const documentElementClientHeight = window.top.document.documentElement.clientHeight
- ? window.top.document.documentElement.clientHeight
- : 0
- const innerHeight = window.top.innerHeight ? window.top.innerHeight : 0
- const outerHeight = window.top.outerHeight ? window.top.outerHeight : 0
- const screenHeight = window.top.screen.height ? window.top.screen.height : 0
+ return req;
+ },
- return documentElementClientHeight || innerHeight || outerHeight || screenHeight
-}
+ imp(buildImp, bidRequest, context) {
+ const imp = buildImp(bidRequest, context);
+ const timeSpentOnPage = document.timeline && document.timeline.currentTime ? document.timeline.currentTime : 0
+ const gpid = bidRequest.adUnitCode;
+ imp.tagid = bidRequest.adUnitCode;
+ imp.ext = mergeDeep({}, bidRequest.params, { timeSpentOnPage, gpid }, imp.ext);
+
+ const bidfloor = getFloor(bidRequest);
+
+ if (!bidfloor) {
+ delete imp.bidfloor;
+ } else {
+ imp.bidfloor = bidfloor;
+ }
+
+ return imp;
+ },
+
+ bidResponse(buildBidResponse, bid, context) {
+ const bidResponse = buildBidResponse(bid, context);
+ bidResponse.currency = 'USD';
+ return bidResponse;
+ }
+});
function isBidRequestValid(bid) {
const adUnitSizes = getAdUnitSizes(bid);
- const isValidSizes = Boolean(adUnitSizes) && adUnitSizes.length > 0;
- const isValidAdUnitId = !!bid.params.adUnitId;
- const isValidAssetKey = !!bid.params.assetKey;
+ const isValidSize = (Boolean(adUnitSizes) && adUnitSizes.length > 0);
+ const hasAssetKeyAndAdUnitId = !!deepAccess(bid, 'params.adUnitId') && !!deepAccess(bid, 'params.assetKey');
+ const hasPublisherIdAndAdUnitCode = !!deepAccess(bid, 'ortb2.site.publisher.id') && !!bid.adUnitCode;
- return (isValidSizes && isValidAdUnitId && isValidAssetKey);
+ return isValidSize && (hasAssetKeyAndAdUnitId || hasPublisherIdAndAdUnitCode);
}
function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) {
@@ -80,126 +111,19 @@ function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent, gpp
return [];
}
-function buildRequests(validBidRequests, bidderRequest) {
- const openRtbBidRequestBanner = {
- id: bidderRequest.bidderRequestId,
- tmax: Math.min(DEFAULT_TIMEOUT, bidderRequest.timeout),
- at: 1,
- regs: {
- ext: {
- gdpr: bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies ? 1 : 0,
- },
- },
- site: {
- domain: location.hostname,
- page: location.href
- },
- user: {
- ext: {
- consent: ''
- }
- },
- imp: [],
- ext: {
- adapterversion: ADAPTER_VERSION,
- prebidversion: '$prebid.version$'
- },
- device: {
- w: getClientWidth(),
- h: getClientHeight(),
- pxratio: window.devicePixelRatio
- }
- };
-
- if (bidderRequest.gdprConsent && bidderRequest.gdprConsent.consentString) {
- openRtbBidRequestBanner.user.ext.consent = bidderRequest.gdprConsent.consentString
- }
- if (bidderRequest.gppConsent && bidderRequest.gppConsent.gppString) {
- openRtbBidRequestBanner.regs.ext.gpp = bidderRequest.gppConsent.gppString
- }
- if (bidderRequest.gppConsent && bidderRequest.gppConsent.applicableSections) {
- openRtbBidRequestBanner.regs.ext.gpp_sid = bidderRequest.gppConsent.applicableSections
- }
-
- validBidRequests.forEach((bidRequest) => {
- const sizes = getAdUnitSizes(bidRequest)
- .map(size => ({ w: size[0], h: size[1] }));
-
- if (bidRequest.mediaTypes &&
- bidRequest.mediaTypes.hasOwnProperty('banner')) {
- openRtbBidRequestBanner.site.id = bidRequest.params.assetKey;
- const floor = getFloor(bidRequest);
-
- if (bidRequest.userId) {
- openRtbBidRequestBanner.user.ext.uids = bidRequest.userId
- }
- if (bidRequest.userIdAsEids) {
- openRtbBidRequestBanner.user.ext.eids = bidRequest.userIdAsEids
- }
-
- const gpid = deepAccess(bidRequest, 'ortb2Imp.ext.gpid');
-
- openRtbBidRequestBanner.imp.push({
- id: bidRequest.bidId,
- tagid: bidRequest.params.adUnitId,
- ...(floor && {bidfloor: floor}),
- banner: {
- format: sizes
- },
- ext: {
- ...bidRequest.params,
- ...(gpid && {gpid}),
- timeSpentOnPage: document.timeline && document.timeline.currentTime ? document.timeline.currentTime : 0
- }
- });
- }
- });
+function buildRequests(bidRequests, bidderRequest) {
+ const data = converter.toORTB({bidRequests, bidderRequest});
return {
method: 'POST',
url: BID_HOST,
- data: openRtbBidRequestBanner,
+ data,
options: {contentType: 'application/json'},
};
}
-function interpretResponse(openRtbBidResponse) {
- if (!openRtbBidResponse ||
- !openRtbBidResponse.body ||
- typeof openRtbBidResponse.body != 'object' ||
- Object.keys(openRtbBidResponse.body).length === 0) {
- logWarn('no response or body is malformed');
- return [];
- }
-
- const bidResponses = [];
-
- openRtbBidResponse.body.seatbid.forEach((seatbid) => {
- seatbid.bid.forEach((bid) => {
- let bidResponse = {
- requestId: bid.impid,
- cpm: bid.price,
- currency: 'USD',
- width: bid.w,
- height: bid.h,
- creativeId: bid.id,
- netRevenue: true,
- ttl: 60,
- ext: bid.ext,
- meta: {
- advertiserDomains: bid.adomain
- },
- nurl: bid.nurl,
- adapterVersion: ADAPTER_VERSION,
- prebidVersion: '$prebid.version$'
- };
-
- bidResponse.ad = bid.adm;
-
- bidResponses.push(bidResponse);
- });
- });
- return bidResponses;
+function interpretResponse(response, request) {
+ return converter.fromORTB({response: response.body, request: request.data}).bids;
}
function getFloor(bid) {
@@ -211,6 +135,7 @@ function getFloor(bid) {
mediaType: 'banner',
size: '*'
});
+
return (isPlainObject(floorResult) && floorResult.currency === 'USD') ? floorResult.floor : 0;
}
diff --git a/modules/omsBidAdapter.js b/modules/omsBidAdapter.js
index 901b9a138d5..de03f65781e 100644
--- a/modules/omsBidAdapter.js
+++ b/modules/omsBidAdapter.js
@@ -13,7 +13,7 @@ import {
formatQS,
} from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
-import {BANNER} from '../src/mediaTypes.js';
+import { BANNER, VIDEO } from '../src/mediaTypes.js';
import {ajax} from '../src/ajax.js';
import {percentInView} from '../libraries/percentInView/percentInView.js';
import {getUserSyncParams} from '../libraries/userSyncUtils/userSyncUtils.js';
@@ -27,7 +27,7 @@ export const spec = {
code: BIDDER_CODE,
aliases: ['brightcom', 'bcmssp'],
gvlid: 883,
- supportedMediaTypes: [BANNER],
+ supportedMediaTypes: [BANNER, VIDEO],
isBidRequestValid,
buildRequests,
interpretResponse,
@@ -39,7 +39,7 @@ export const spec = {
function buildRequests(bidReqs, bidderRequest) {
try {
const impressions = bidReqs.map(bid => {
- let bidSizes = bid?.mediaTypes?.banner?.sizes || bid.sizes;
+ let bidSizes = bid?.mediaTypes?.banner?.sizes || bid?.mediaTypes?.video?.playerSize || bid.sizes;
bidSizes = ((isArray(bidSizes) && isArray(bidSizes[0])) ? bidSizes : [bidSizes]);
bidSizes = bidSizes.filter(size => isArray(size));
const processedSizes = bidSizes.map(size => ({w: parseInt(size[0], 10), h: parseInt(size[1], 10)}));
@@ -52,18 +52,25 @@ function buildRequests(bidReqs, bidderRequest) {
const imp = {
id: bid.bidId,
- banner: {
- format: processedSizes,
- ext: {
- viewability: viewabilityAmountRounded,
- }
- },
ext: {
...gpidData
},
tagid: String(bid.adUnitCode)
};
+ if (bid?.mediaTypes?.video) {
+ imp.video = {
+ ...bid.mediaTypes.video,
+ }
+ } else {
+ imp.banner = {
+ format: processedSizes,
+ ext: {
+ viewability: viewabilityAmountRounded,
+ }
+ }
+ }
+
const bidFloor = _getBidFloor(bid);
if (bidFloor) {
@@ -158,7 +165,7 @@ function interpretResponse(serverResponse) {
try {
if (id && seatbid && seatbid.length > 0 && seatbid[0].bid && seatbid[0].bid.length > 0) {
response = seatbid[0].bid.map(bid => {
- return {
+ const bidResponse = {
requestId: bid.impid,
cpm: parseFloat(bid.price),
width: parseInt(bid.w),
@@ -166,13 +173,20 @@ function interpretResponse(serverResponse) {
creativeId: bid.crid || bid.id,
currency: 'USD',
netRevenue: true,
- mediaType: BANNER,
ad: _getAdMarkup(bid),
ttl: 300,
meta: {
advertiserDomains: bid?.adomain || []
}
};
+
+ if (bid.mtype === 2) {
+ bidResponse.mediaType = VIDEO;
+ } else {
+ bidResponse.mediaType = BANNER;
+ }
+
+ return bidResponse;
});
}
} catch (e) {
diff --git a/modules/omsBidAdapter.md b/modules/omsBidAdapter.md
index 0a6b9cac82c..506ba5fdbd5 100644
--- a/modules/omsBidAdapter.md
+++ b/modules/omsBidAdapter.md
@@ -41,6 +41,21 @@ var adUnits = [
publisherId: 2141020
}
}]
+ },
+ {
+ code: 'video-instream',
+ mediaTypes: {
+ video: {
+ context: 'instream',
+ playerSize: [640, 480]
+ }
+ },
+ bids: [{
+ bidder: 'oms',
+ params: {
+ publisherId: 2141020
+ }
+ }]
}
]
```
diff --git a/modules/openwebBidAdapter.js b/modules/openwebBidAdapter.js
index 60364f41d3c..0dff314ce17 100644
--- a/modules/openwebBidAdapter.js
+++ b/modules/openwebBidAdapter.js
@@ -1,35 +1,19 @@
-import {
- logWarn,
- logInfo,
- isArray,
- deepAccess,
- triggerPixel
-} from '../src/utils.js';
-import { registerBidder } from '../src/adapters/bidderFactory.js';
-import { BANNER, VIDEO } from '../src/mediaTypes.js';
-import {
- getEndpoint,
- generateBidsParams,
- generateGeneralParams,
- buildBidResponse,
-} from '../libraries/riseUtils/index.js';
+import {logWarn} from '../src/utils.js';
+import {registerBidder} from '../src/adapters/bidderFactory.js';
+import {makeBaseSpec} from '../libraries/riseUtils/index.js';
-const SUPPORTED_AD_TYPES = [BANNER, VIDEO];
const BIDDER_CODE = 'openweb';
-const ADAPTER_VERSION = '6.0.0';
-const TTL = 360;
-const DEFAULT_CURRENCY = 'USD';
const BASE_URL = 'https://hb.openwebmp.com/';
+const GVLID = 280;
const MODES = {
PRODUCTION: 'hb-multi',
TEST: 'hb-multi-test'
};
export const spec = {
+ ...makeBaseSpec(BASE_URL, MODES),
code: BIDDER_CODE,
- gvlid: 280,
- version: ADAPTER_VERSION,
- supportedMediaTypes: SUPPORTED_AD_TYPES,
+ gvlid: GVLID,
isBidRequestValid: function (bidRequest) {
if (!bidRequest.params) {
logWarn('no params have been set to OpenWeb adapter');
@@ -47,65 +31,6 @@ export const spec = {
}
return true;
- },
- buildRequests: function (validBidRequests, bidderRequest) {
- const combinedRequestsObject = {};
-
- // use data from the first bid, to create the general params for all bids
- const generalObject = validBidRequests[0];
- const testMode = generalObject.params.testMode;
-
- combinedRequestsObject.params = generateGeneralParams(generalObject, bidderRequest);
- combinedRequestsObject.bids = generateBidsParams(validBidRequests, bidderRequest);
-
- return {
- method: 'POST',
- url: getEndpoint(testMode, BASE_URL, MODES),
- data: combinedRequestsObject
- }
- },
- interpretResponse: function ({body}) {
- const bidResponses = [];
-
- if (body && body.bids && body.bids.length) {
- body.bids.forEach(adUnit => {
- const bidResponse = buildBidResponse(adUnit, DEFAULT_CURRENCY, TTL, VIDEO, BANNER);
- bidResponses.push(bidResponse);
- });
- }
-
- return bidResponses;
- },
- getUserSyncs: function (syncOptions, serverResponses) {
- const syncs = [];
- for (const response of serverResponses) {
- if (syncOptions.iframeEnabled && deepAccess(response, 'body.params.userSyncURL')) {
- syncs.push({
- type: 'iframe',
- url: deepAccess(response, 'body.params.userSyncURL')
- });
- }
- if (syncOptions.pixelEnabled && isArray(deepAccess(response, 'body.params.userSyncPixels'))) {
- const pixels = response.body.params.userSyncPixels.map(pixel => {
- return {
- type: 'image',
- url: pixel
- }
- });
- syncs.push(...pixels);
- }
- }
- return syncs;
- },
- onBidWon: function (bid) {
- if (bid == null) {
- return;
- }
-
- logInfo('onBidWon:', bid);
- if (bid.hasOwnProperty('nurl') && bid.nurl.length > 0) {
- triggerPixel(bid.nurl);
- }
}
};
diff --git a/modules/openwebBidAdapter.md b/modules/openwebBidAdapter.md
index 5450182265c..c5bc10c3c12 100644
--- a/modules/openwebBidAdapter.md
+++ b/modules/openwebBidAdapter.md
@@ -13,7 +13,7 @@ Module that connects to OpenWeb's demand sources.
The OpenWeb adapter requires setup and approval from OpenWeb. Please reach out to monetization@openweb.com to create an OpenWeb account.
-The adapter supports Video and Display demand.
+The adapter supports Video(instream), Banner, Native and multi-format bid requests.
# Bid Parameters
## Video
diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js
index 19da19e661f..292df1d4152 100644
--- a/modules/openxBidAdapter.js
+++ b/modules/openxBidAdapter.js
@@ -2,8 +2,9 @@ import {config} from '../src/config.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import * as utils from '../src/utils.js';
import {mergeDeep} from '../src/utils.js';
-import {BANNER, VIDEO} from '../src/mediaTypes.js';
+import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
import {ortbConverter} from '../libraries/ortbConverter/converter.js';
+import {ORTB_MTYPES} from '../libraries/ortbConverter/processors/mediaType.js';
const bidderConfig = 'hb_pb_ortb';
const bidderVersion = '2.0';
@@ -13,7 +14,7 @@ export const DEFAULT_PH = '2d1251ae-7f3a-47cf-bd2a-2f288854a0ba';
export const spec = {
code: 'openx',
gvlid: 69,
- supportedMediaTypes: [BANNER, VIDEO],
+ supportedMediaTypes: [BANNER, VIDEO, NATIVE],
isBidRequestValid,
buildRequests,
interpretResponse,
@@ -25,7 +26,12 @@ registerBidder(spec);
const converter = ortbConverter({
context: {
netRevenue: true,
- ttl: 300
+ ttl: 300,
+ nativeRequest: {
+ eventtrackers: [
+ {event: 1, methods: [1, 2]},
+ ]
+ }
},
imp(buildImp, bidRequest, context) {
const imp = buildImp(bidRequest, context);
@@ -74,6 +80,18 @@ const converter = ortbConverter({
return req;
},
bidResponse(buildBidResponse, bid, context) {
+ if (!context.mediaType && !bid.mtype) {
+ let mediaType = BANNER; // default media type
+ const vastKeywords = ['VAST ', 'vast ', 'videoad', 'VAST_VERSION', 'dc_vast', 'video '];
+ if (bid.adm && bid.adm.startsWith('{') && bid.adm.includes('"assets"')) {
+ mediaType = NATIVE;
+ } else if (bid.vastXml || bid.vastUrl || (bid.adm && vastKeywords.some(v => bid.adm.includes(v)))) {
+ mediaType = VIDEO;
+ }
+ bid.mediaType = mediaType;
+ bid.mtype = Object.keys(ORTB_MTYPES).find(key => ORTB_MTYPES[key] === mediaType);
+ }
+
const bidResponse = buildBidResponse(bid, context);
if (bid.ext) {
bidResponse.meta.networkId = bid.ext.dsp_id;
@@ -158,8 +176,11 @@ function isBidRequestValid(bidRequest) {
function buildRequests(bids, bidderRequest) {
let videoBids = bids.filter(bid => isVideoBid(bid));
- let bannerBids = bids.filter(bid => isBannerBid(bid));
- let requests = bannerBids.length ? [createRequest(bannerBids, bidderRequest, BANNER)] : [];
+ let bannerAndNativeBids = bids.filter(bid => isBannerBid(bid) || isNativeBid(bid))
+ // In case of multi-format bids remove `video` from mediaTypes as for video a separate bid request is built
+ .map(bid => ({...bid, mediaTypes: {...bid.mediaTypes, video: undefined}}));
+
+ let requests = bannerAndNativeBids.length ? [createRequest(bannerAndNativeBids, bidderRequest, null)] : [];
videoBids.forEach(bid => {
requests.push(createRequest([bid], bidderRequest, VIDEO));
});
@@ -178,8 +199,13 @@ function isVideoBid(bid) {
return utils.deepAccess(bid, 'mediaTypes.video');
}
+function isNativeBid(bid) {
+ return utils.deepAccess(bid, 'mediaTypes.native');
+}
+
function isBannerBid(bid) {
- return utils.deepAccess(bid, 'mediaTypes.banner') || !isVideoBid(bid);
+ const isNotVideoOrNativeBid = !isVideoBid(bid) && !isNativeBid(bid)
+ return utils.deepAccess(bid, 'mediaTypes.banner') || isNotVideoOrNativeBid;
}
function interpretResponse(resp, req) {
diff --git a/modules/openxBidAdapter.md b/modules/openxBidAdapter.md
index a39aa1580cd..61b6426d113 100644
--- a/modules/openxBidAdapter.md
+++ b/modules/openxBidAdapter.md
@@ -34,6 +34,16 @@ Please note you should only include either openxBidAdapter or openxOrtbBidAdapte
| `delDomain` | required | String | OpenX delivery domain provided by your OpenX representative. | "PUBLISHER-d.openx.net"
| `video` | optional | OpenRTB video subtypes | Use of adUnit.mediaTypes.video is now preferred. | `{ video: {mimes: ['video/mp4']}`
+## Native
+
+| Name | Scope | Type | Description | Example
+| ---- | ----- | ---- | ----------- | -------
+| `delDomain` or `platform` | required | String | OpenX delivery domain or platform id provided by your OpenX representative. | "PUBLISHER-d.openx.net" or "555not5a-real-plat-form-id0123456789"
+| `unit` | required | String | OpenX ad unit ID provided by your OpenX representative. | "1611023122"
+| `customParams` | optional | Object | User-defined targeting key-value pairs. customParams applies to a specific unit. | `{key1: "v1", key2: ["v2","v3"]}`
+| `customFloor` | optional | Number | Minimum price in USD. customFloor applies to a specific unit. For example, use the following value to set a $1.50 floor: 1.50
**WARNING:** Misuse of this parameter can impact revenue | 1.50
+| `doNotTrack` | optional | Boolean | Prevents advertiser from using data for this user.