• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prebid / Prebid.js / 20074850237

09 Dec 2025 06:49PM UTC coverage: 96.215%. Remained the same
20074850237

push

github

web-flow
sevioBidAdapter: change how pageTitle and pageDescription are sent to the BE (#14247)

* Change the context format sent to the BE

* Bump adapter version

53873 of 65994 branches covered (81.63%)

3 of 4 new or added lines in 2 files covered. (75.0%)

6 existing lines in 1 file now uncovered.

206139 of 214249 relevant lines covered (96.21%)

71.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.57
/modules/sovrnBidAdapter.js
1
import {
1✔
2
  _each,
3
  isArray,
4
  getUniqueIdentifierStr,
5
  deepSetValue,
6
  logError,
7
  deepAccess,
8
  isInteger,
9
  logWarn,
10
  getBidIdParameter,
11
  mergeDeep
12
} from '../src/utils.js';
13
import { registerBidder } from '../src/adapters/bidderFactory.js'
14
import {
15
  BANNER,
16
  VIDEO
17
} from '../src/mediaTypes.js'
18
import { COMMON_ORTB_VIDEO_PARAMS } from '../libraries/deepintentUtils/index.js';
19

20
/**
21
 * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid
22
 */
23

24
const ORTB_VIDEO_PARAMS = {
1✔
25
  ...COMMON_ORTB_VIDEO_PARAMS,
UNCOV
26
  'placement': (value) => isInteger(value) && value >= 1 && value <= 5,
×
27
  'plcmt': (value) => isInteger(value) && value >= 1 && value <= 4,
×
28
  'delivery': (value) => Array.isArray(value) && value.every(v => v >= 1 && v <= 3),
×
29
  'pos': (value) => isInteger(value) && value >= 1 && value <= 7,
×
30
}
31

32
const REQUIRED_VIDEO_PARAMS = {
1✔
33
  mimes: ORTB_VIDEO_PARAMS.mimes,
34
  maxduration: ORTB_VIDEO_PARAMS.maxduration,
35
  protocols: ORTB_VIDEO_PARAMS.protocols
36
}
37

38
export const spec = {
1✔
39
  code: 'sovrn',
40
  supportedMediaTypes: [BANNER, VIDEO],
41
  gvlid: 13,
42

43
  /**
44
   * Check if the bid is a valid zone ID in either number or string form
45
   * @param {object} bid the Sovrn bid to validate
46
   * @return boolean for whether or not a bid is valid
47
   */
48
  isBidRequestValid: function (bid) {
49
    const video = bid?.mediaTypes?.video
5✔
50
    return !!(
5✔
51
      bid.params.tagid &&
17✔
52
      !isNaN(parseFloat(bid.params.tagid)) &&
53
      isFinite(bid.params.tagid) && (
54
        !video || (
55
          Object.keys(REQUIRED_VIDEO_PARAMS)
56
            .every(key => REQUIRED_VIDEO_PARAMS[key](video[key]))
4✔
57
        )
58
      )
59
    )
60
  },
61

62
  /**
63
   * Format the bid request object for our endpoint
64
   * @return object of parameters for Prebid AJAX request
65
   * @param bidReqs
66
   * @param bidderRequest
67
   */
68
  buildRequests: function(bidReqs, bidderRequest) {
69
    try {
32✔
70
      const sovrnImps = [];
32✔
71
      let iv;
72
      let schain;
73
      let eids;
74
      let criteoId;
75

76
      _each(bidReqs, function (bid) {
32✔
77
        if (!eids && bid.userIdAsEids) {
34✔
78
          eids = bid.userIdAsEids;
1✔
79
          eids.forEach(function (id) {
1✔
80
            if (id.uids && id.uids[0]) {
2✔
81
              if (id.source === 'criteo.com') {
2✔
82
                criteoId = id.uids[0].id
1✔
83
              }
84
            }
85
          })
86
        }
87

88
        const bidSchain = bid?.ortb2?.source?.ext?.schain;
34✔
89
        if (bidSchain) {
34✔
90
          schain = schain || bidSchain
1✔
91
        }
92
        iv = iv || getBidIdParameter('iv', bid.params)
34✔
93

94
        const imp = {
34✔
95
          adunitcode: bid.adUnitCode,
96
          id: bid.bidId,
97
          tagid: String(getBidIdParameter('tagid', bid.params)),
98
          bidfloor: _getBidFloors(bid)
99
        }
100

101
        if (deepAccess(bid, 'mediaTypes.banner')) {
34✔
102
          let bidSizes = deepAccess(bid, 'mediaTypes.banner.sizes') || bid.sizes
3✔
103
          bidSizes = (isArray(bidSizes) && isArray(bidSizes[0])) ? bidSizes : [bidSizes]
3✔
104
          bidSizes = bidSizes.filter(size => isArray(size))
5✔
105
          const processedSizes = bidSizes.map(size => ({w: parseInt(size[0], 10), h: parseInt(size[1], 10)}))
5✔
106

107
          imp.banner = {
3✔
108
            format: processedSizes,
109
            w: 1,
110
            h: 1,
111
          };
112
        }
113
        if (deepAccess(bid, 'mediaTypes.video')) {
34✔
114
          imp.video = _buildVideoRequestObj(bid);
2✔
115
        }
116

117
        imp.ext = getBidIdParameter('ext', bid.ortb2Imp) || undefined
34✔
118

119
        const segmentsString = getBidIdParameter('segments', bid.params)
34✔
120
        if (segmentsString) {
34✔
121
          imp.ext = imp.ext || {}
2✔
122
          imp.ext.deals = segmentsString.split(',').map(deal => deal.trim())
4✔
123
        }
124
        sovrnImps.push(imp)
34✔
125
      })
126

127
      const fpd = bidderRequest.ortb2 || {};
32✔
128

129
      const site = fpd.site || {}
32✔
130
      site.page = bidderRequest.refererInfo.page
32✔
131
      site.domain = bidderRequest.refererInfo.domain
32✔
132

133
      const tmax = deepAccess(bidderRequest, 'timeout');
32✔
134

135
      const sovrnBidReq = {
32✔
136
        id: getUniqueIdentifierStr(),
137
        imp: sovrnImps,
138
        site: site,
139
        user: fpd.user || {},
63✔
140
        tmax: tmax
141
      }
142

143
      if (schain) {
32✔
144
        sovrnBidReq.source = {
1✔
145
          ext: {
146
            schain
147
          }
148
        };
149
      }
150

151
      const tid = deepAccess(bidderRequest, 'ortb2.source.tid')
32✔
152
      if (tid) {
32✔
153
        deepSetValue(sovrnBidReq, 'source.tid', tid)
1✔
154
      }
155

156
      const coppa = deepAccess(bidderRequest, 'ortb2.regs.coppa');
32✔
157
      if (coppa) {
32✔
158
        deepSetValue(sovrnBidReq, 'regs.coppa', 1);
1✔
159
      }
160

161
      const bcat = deepAccess(bidderRequest, 'ortb2.bcat');
32✔
162
      if (bcat) {
32✔
163
        deepSetValue(sovrnBidReq, 'bcat', bcat);
1✔
164
      }
165

166
      if (bidderRequest.gdprConsent) {
32✔
167
        deepSetValue(sovrnBidReq, 'regs.ext.gdpr', +bidderRequest.gdprConsent.gdprApplies);
5✔
168
        deepSetValue(sovrnBidReq, 'user.ext.consent', bidderRequest.gdprConsent.consentString)
5✔
169
      }
170
      if (bidderRequest.uspConsent) {
32✔
171
        deepSetValue(sovrnBidReq, 'regs.ext.us_privacy', bidderRequest.uspConsent);
1✔
172
      }
173
      if (bidderRequest.gppConsent) {
32✔
174
        deepSetValue(sovrnBidReq, 'regs.gpp', bidderRequest.gppConsent.gppString);
2✔
175
        deepSetValue(sovrnBidReq, 'regs.gpp_sid', bidderRequest.gppConsent.applicableSections);
2✔
176
      }
177

178
      // if present, merge device object from ortb2 into `sovrnBidReq.device`
179
      if (bidderRequest?.ortb2?.device) {
32✔
180
        sovrnBidReq.device = sovrnBidReq.device || {};
1✔
181
        mergeDeep(sovrnBidReq.device, bidderRequest.ortb2.device);
1✔
182
      }
183

184
      if (eids) {
32✔
185
        deepSetValue(sovrnBidReq, 'user.ext.eids', eids)
1✔
186
        if (criteoId) {
1✔
187
          deepSetValue(sovrnBidReq, 'user.ext.prebid_criteoid', criteoId)
1✔
188
        }
189
      }
190

191
      let url = `https://ap.lijit.com/rtb/bid?src=$$REPO_AND_VERSION$$`;
32✔
192
      if (iv) url += `&iv=${iv}`;
32✔
193

194
      return {
32✔
195
        method: 'POST',
196
        url: url,
197
        data: JSON.stringify(sovrnBidReq),
198
        options: {contentType: 'text/plain'}
199
      }
200
    } catch (e) {
UNCOV
201
      logError('Could not build bidrequest, error deatils:', e);
×
202
    }
203
  },
204

205
  /**
206
   * Format Sovrn responses as Prebid bid responses
207
   * @param {*} param0 A successful response from Sovrn.
208
   * @return {Bid[]} An array of formatted bids.
209
   */
210
  interpretResponse: function({ body: {id, seatbid} }) {
14✔
211
    if (!id || !seatbid || !Array.isArray(seatbid)) return []
14!
212

213
    try {
14✔
214
      return seatbid
14✔
215
        .filter(seat => seat)
14✔
216
        .map(seat => seat.bid.map(sovrnBid => {
14✔
217
          const bid = {
14✔
218
            requestId: sovrnBid.impid,
219
            cpm: parseFloat(sovrnBid.price),
220
            width: parseInt(sovrnBid.w),
221
            height: parseInt(sovrnBid.h),
222
            creativeId: sovrnBid.crid || sovrnBid.id,
16✔
223
            dealId: sovrnBid.dealid || null,
26✔
224
            currency: 'USD',
225
            netRevenue: true,
226
            mediaType: Number(sovrnBid.mtype) === 2 ? VIDEO : BANNER,
14✔
227
            ttl: sovrnBid.ext?.ttl || 90,
68✔
228
            meta: { advertiserDomains: sovrnBid && sovrnBid.adomain ? sovrnBid.adomain : [] }
42!
229
          }
230

231
          if (Number(sovrnBid.mtype) === 2) {
14✔
232
            bid.vastXml = decodeURIComponent(sovrnBid.adm)
5✔
233
          } else {
234
            bid.ad = sovrnBid.nurl ? decodeURIComponent(`${sovrnBid.adm}<img src="${sovrnBid.nurl}">`) : decodeURIComponent(sovrnBid.adm)
9✔
235
          }
236

237
          return bid
14✔
238
        }))
239
        .flat()
240
    } catch (e) {
UNCOV
241
      logError('Could not interpret bidresponse, error details:', e)
×
UNCOV
242
      return e
×
243
    }
244
  },
245

246
  getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) {
247
    try {
8✔
248
      const tracks = []
8✔
249
      if (serverResponses && serverResponses.length !== 0) {
8✔
250
        if (syncOptions.iframeEnabled) {
7✔
251
          const iidArr = serverResponses.filter(resp => deepAccess(resp, 'body.ext.iid'))
5✔
252
            .map(resp => resp.body.ext.iid);
5✔
253
          const params = [];
5✔
254
          if (gdprConsent && gdprConsent.gdprApplies && typeof gdprConsent.consentString === 'string') {
5✔
255
            params.push(['gdpr_consent', gdprConsent.consentString]);
2✔
256
          }
257
          if (uspConsent) {
5✔
258
            params.push(['us_privacy', uspConsent]);
2✔
259
          }
260
          if (gppConsent) {
5✔
261
            params.push(['gpp', gppConsent.gppString]);
2✔
262
            params.push(['gpp_sid', gppConsent.applicableSections])
2✔
263
          }
264

265
          if (iidArr[0]) {
5✔
266
            params.push(['informer', iidArr[0]]);
5✔
267
            tracks.push({
5✔
268
              type: 'iframe',
269
              url: 'https://ce.lijit.com/beacon?' + params.map(p => p.join('=')).join('&')
13✔
270
            });
271
          }
272
        }
273

274
        if (syncOptions.pixelEnabled) {
7✔
275
          serverResponses.filter(resp => deepAccess(resp, 'body.ext.sync.pixels'))
2✔
276
            .reduce((acc, resp) => acc.concat(resp.body.ext.sync.pixels), [])
2✔
277
            .map(pixel => pixel.url)
4✔
278
            .forEach(url => tracks.push({ type: 'image', url }))
4✔
279
        }
280
      }
281
      return tracks
8✔
282
    } catch (e) {
UNCOV
283
      return []
×
284
    }
285
  },
286
}
287

288
function _buildVideoRequestObj(bid) {
289
  const videoObj = {}
2✔
290
  const bidSizes = deepAccess(bid, 'sizes')
2✔
291
  const videoAdUnitParams = deepAccess(bid, 'mediaTypes.video', {})
2✔
292
  const videoBidderParams = deepAccess(bid, 'params.video', {})
2✔
293
  const computedParams = {}
2✔
294

295
  if (bidSizes) {
2✔
296
    const sizes = (Array.isArray(bidSizes[0])) ? bidSizes[0] : bidSizes
1!
297
    computedParams.w = sizes[0]
1✔
298
    computedParams.h = sizes[1]
1✔
299
  } else if (Array.isArray(videoAdUnitParams.playerSize)) {
1✔
300
    const sizes = (Array.isArray(videoAdUnitParams.playerSize[0])) ? videoAdUnitParams.playerSize[0] : videoAdUnitParams.playerSize
1!
301
    computedParams.w = sizes[0]
1✔
302
    computedParams.h = sizes[1]
1✔
303
  }
304

305
  const videoParams = {
2✔
306
    ...computedParams,
307
    ...videoAdUnitParams,
308
    ...videoBidderParams
309
  };
310

311
  Object.keys(ORTB_VIDEO_PARAMS).forEach(paramName => {
2✔
312
    if (videoParams.hasOwnProperty(paramName)) {
48✔
313
      if (ORTB_VIDEO_PARAMS[paramName](videoParams[paramName])) {
14!
314
        videoObj[paramName] = videoParams[paramName]
14✔
315
      } else {
UNCOV
316
        logWarn(`The OpenRTB video param ${paramName} has been skipped due to misformating. Please refer to OpenRTB 2.5 spec.`);
×
317
      }
318
    }
319
  })
320
  return videoObj
2✔
321
}
322

323
function _getBidFloors(bid) {
324
  const floorInfo = (bid.getFloor && typeof bid.getFloor === 'function') ? bid.getFloor({
34✔
325
    currency: 'USD',
326
    mediaType: bid.mediaTypes && bid.mediaTypes.banner ? 'banner' : 'video',
8!
327
    size: '*'
328
  }) : {}
329
  const floorModuleValue = parseFloat(floorInfo?.floor)
34!
330
  if (!isNaN(floorModuleValue)) {
34✔
331
    return floorModuleValue
1✔
332
  }
333
  const paramValue = parseFloat(getBidIdParameter('bidfloor', bid.params))
33✔
334
  return !isNaN(paramValue) ? paramValue : undefined
33✔
335
}
336

337
registerBidder(spec)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc