prebid/Prebid.js

View on GitHub
modules/undertoneBidAdapter.js

Summary

Maintainability
C
1 day
Test Coverage
/**
 * Adapter to send bids to Undertone
 */

import {deepAccess, parseUrl, extractDomainFromHost} from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';

const BIDDER_CODE = 'undertone';
const URL = 'https://hb.undertone.com/hb';
const FRAME_USER_SYNC = 'https://cdn.undertone.com/js/usersync.html';
const PIXEL_USER_SYNC_1 = 'https://usr.undertone.com/userPixel/syncOne?id=1&of=2';
const PIXEL_USER_SYNC_2 = 'https://usr.undertone.com/userPixel/syncOne?id=2&of=2';

function getBidFloor(bidRequest, mediaType) {
  if (typeof bidRequest.getFloor !== 'function') {
    return 0;
  }

  const floor = bidRequest.getFloor({
    currency: 'USD',
    mediaType: mediaType,
    size: '*'
  });

  return (floor && floor.currency === 'USD' && floor.floor) || 0;
}

function getGdprQueryParams(gdprConsent) {
  if (!gdprConsent) {
    return null;
  }

  let gdpr = gdprConsent.gdprApplies ? '1' : '0';
  let gdprstr = gdprConsent.consentString ? gdprConsent.consentString : '';
  return `gdpr=${gdpr}&gdprstr=${gdprstr}`;
}

function getBannerCoords(id) {
  let element = document.getElementById(id);
  let left = -1;
  let top = -1;
  if (element) {
    left = element.offsetLeft;
    top = element.offsetTop;

    let parent = element.offsetParent;
    if (parent) {
      left += parent.offsetLeft;
      top += parent.offsetTop;
    }

    return [left, top];
  } else {
    return null;
  }
}

export const spec = {
  code: BIDDER_CODE,
  gvlid: 677,
  supportedMediaTypes: [BANNER, VIDEO],
  isBidRequestValid: function(bid) {
    if (bid && bid.params && bid.params.publisherId) {
      bid.params.publisherId = parseInt(bid.params.publisherId);
      return true;
    }
  },
  buildRequests: function(validBidRequests, bidderRequest) {
    const vw = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
    const vh = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
    const pageSizeArray = vw == 0 || vh == 0 ? null : [vw, vh];
    const commons = {
      'adapterVersion': '$prebid.version$',
      'uids': validBidRequests[0].userId,
      'pageSize': pageSizeArray
    };
    if (validBidRequests[0].schain) {
      commons.schain = validBidRequests[0].schain;
    }
    const payload = {
      'x-ut-hb-params': [],
      'commons': commons
    };
    const referer = bidderRequest.refererInfo.topmostLocation;
    const canonicalUrl = bidderRequest.refererInfo.canonicalUrl;
    if (referer) {
      commons.referrer = referer;
    }
    if (canonicalUrl) {
      commons.canonicalUrl = canonicalUrl;
    }
    const hostname = parseUrl(referer).hostname;
    let domain = extractDomainFromHost(hostname);
    const pageUrl = canonicalUrl || referer;

    const pubid = validBidRequests[0].params.publisherId;
    let reqUrl = `${URL}?pid=${pubid}&domain=${domain}`;

    let gdprParams = getGdprQueryParams(bidderRequest.gdprConsent);
    if (gdprParams) {
      reqUrl += `&${gdprParams}`;
    }

    if (bidderRequest.uspConsent) {
      reqUrl += `&ccpa=${bidderRequest.uspConsent}`;
    }

    if (bidderRequest.gppConsent) {
      const gppString = bidderRequest.gppConsent.gppString ?? '';
      const ggpSid = bidderRequest.gppConsent.applicableSections ?? '';
      reqUrl += `&gpp=${gppString}&gpp_sid=${ggpSid}`;
    }

    validBidRequests.map(bidReq => {
      const bid = {
        bidRequestId: bidReq.bidId,
        coordinates: getBannerCoords(bidReq.adUnitCode),
        hbadaptor: 'prebid',
        url: pageUrl,
        domain: domain,
        placementId: bidReq.params.placementId != undefined ? bidReq.params.placementId : null,
        publisherId: bidReq.params.publisherId,
        gpid: deepAccess(bidReq, 'ortb2Imp.ext.gpid', deepAccess(bidReq, 'ortb2Imp.ext.data.pbadslot', '')),
        sizes: bidReq.sizes,
        params: bidReq.params
      };
      const videoMediaType = deepAccess(bidReq, 'mediaTypes.video');
      const mediaType = videoMediaType ? VIDEO : BANNER;
      bid.mediaType = mediaType;
      bid.bidfloor = getBidFloor(bidReq, mediaType);
      if (videoMediaType) {
        bid.video = {
          playerSize: deepAccess(bidReq, 'mediaTypes.video.playerSize') || null,
          streamType: deepAccess(bidReq, 'mediaTypes.video.context') || null,
          playbackMethod: deepAccess(bidReq, 'params.video.playbackMethod') || null,
          maxDuration: deepAccess(bidReq, 'params.video.maxDuration') || null,
          skippable: deepAccess(bidReq, 'params.video.skippable') || null,
          placement: deepAccess(bidReq, 'mediaTypes.video.placement') || null,
          plcmt: deepAccess(bidReq, 'mediaTypes.video.plcmt') || null
        };
      }
      payload['x-ut-hb-params'].push(bid);
    });

    return {
      method: 'POST',
      url: reqUrl,
      withCredentials: true,
      data: JSON.stringify(payload)
    };
  },
  interpretResponse: function(serverResponse, request) {
    const bids = [];
    const body = serverResponse.body;

    if (body && Array.isArray(body) && body.length > 0) {
      body.forEach((bidRes) => {
        if (bidRes.ad && bidRes.cpm > 0) {
          const bid = {
            requestId: bidRes.bidRequestId,
            cpm: bidRes.cpm,
            width: bidRes.width,
            height: bidRes.height,
            creativeId: bidRes.adId,
            currency: bidRes.currency,
            netRevenue: bidRes.netRevenue,
            ttl: bidRes.ttl || 360,
            meta: { advertiserDomains: bidRes.adomain ? bidRes.adomain : [] }
          };
          if (bidRes.mediaType && bidRes.mediaType === 'video') {
            bid.vastXml = bidRes.ad;
            bid.mediaType = bidRes.mediaType;
          } else {
            bid.ad = bidRes.ad
          }
          bids.push(bid);
        }
      });
    }
    return bids;
  },
  getUserSyncs: function(syncOptions, serverResponses, gdprConsent, usPrivacy) {
    const syncs = [];

    let gdprParams = getGdprQueryParams(gdprConsent);
    let iframePrivacyParams = '';
    let pixelPrivacyParams = '';

    if (gdprParams) {
      iframePrivacyParams += `?${gdprParams}`;
      pixelPrivacyParams += `&${gdprParams}`;
    }

    if (usPrivacy) {
      if (iframePrivacyParams != '') {
        iframePrivacyParams += '&'
      } else {
        iframePrivacyParams += '?'
      }
      iframePrivacyParams += `ccpa=${usPrivacy}`;
      pixelPrivacyParams += `&ccpa=${usPrivacy}`;
    }

    if (syncOptions.iframeEnabled) {
      syncs.push({
        type: 'iframe',
        url: FRAME_USER_SYNC + iframePrivacyParams
      });
    } else if (syncOptions.pixelEnabled) {
      syncs.push({
        type: 'image',
        url: PIXEL_USER_SYNC_1 + pixelPrivacyParams
      },
      {
        type: 'image',
        url: PIXEL_USER_SYNC_2 + pixelPrivacyParams
      });
    }
    return syncs;
  }
};
registerBidder(spec);