prebid/Prebid.js

View on GitHub
modules/ccxBidAdapter.js

Summary

Maintainability
D
2 days
Test Coverage
import {_each, deepAccess, isArray, isEmpty, logWarn} from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {getStorageManager} from '../src/storageManager.js';

const BIDDER_CODE = 'ccx'
const storage = getStorageManager({bidderCode: BIDDER_CODE});
const BID_URL = 'https://delivery.clickonometrics.pl/ortb/prebid/bid'
const GVLID = 773;
const SUPPORTED_VIDEO_PROTOCOLS = [2, 3, 5, 6]
const SUPPORTED_VIDEO_MIMES = ['video/mp4', 'video/x-flv']
const SUPPORTED_VIDEO_PLAYBACK_METHODS = [1, 2, 3, 4]

function _getDeviceObj () {
  let device = {}
  device.w = screen.width
  device.y = screen.height
  device.ua = navigator.userAgent
  return device
}

function _getSiteObj (bidderRequest) {
  let site = {}
  let url = bidderRequest?.refererInfo?.page || ''
  if (url.length > 0) {
    url = url.split('?')[0]
  }
  site.page = url

  return site
}

function _validateSizes (sizeObj, type) {
  if (!isArray(sizeObj) || typeof sizeObj[0] === 'undefined') {
    return false
  }

  if (type === 'video' && (!isArray(sizeObj[0]) || sizeObj[0].length !== 2)) {
    return false
  }

  let result = true

  if (type === 'banner') {
    _each(sizeObj, function (size) {
      if (!isArray(size) || (size.length !== 2)) {
        result = false
      }
    })
    return result
  }

  if (type === 'old') {
    if (!isArray(sizeObj[0]) && sizeObj.length !== 2) {
      result = false
    } else if (isArray(sizeObj[0])) {
      _each(sizeObj, function (size) {
        if (!isArray(size) || (size.length !== 2)) {
          result = false
        }
      })
    }
    return result;
  }

  return true
}

function _buildBid (bid, bidderRequest) {
  let placement = {}
  placement.id = bid.bidId
  placement.secure = 1

  let sizes = deepAccess(bid, 'mediaTypes.banner.sizes') || deepAccess(bid, 'mediaTypes.video.playerSize') || deepAccess(bid, 'sizes')

  if (deepAccess(bid, 'mediaTypes.banner') || deepAccess(bid, 'mediaType') === 'banner' || (!deepAccess(bid, 'mediaTypes.video') && !deepAccess(bid, 'mediaType'))) {
    placement.banner = {'format': []}
    if (isArray(sizes[0])) {
      _each(sizes, function (size) {
        placement.banner.format.push({'w': size[0], 'h': size[1]})
      })
    } else {
      placement.banner.format.push({'w': sizes[0], 'h': sizes[1]})
    }
  } else if (deepAccess(bid, 'mediaTypes.video') || deepAccess(bid, 'mediaType') === 'video') {
    placement.video = {}

    if (typeof sizes !== 'undefined') {
      if (isArray(sizes[0])) {
        placement.video.w = sizes[0][0]
        placement.video.h = sizes[0][1]
      } else {
        placement.video.w = sizes[0]
        placement.video.h = sizes[1]
      }
    }

    placement.video.protocols = deepAccess(bid, 'mediaTypes.video.protocols') || deepAccess(bid, 'params.video.protocols') || SUPPORTED_VIDEO_PROTOCOLS
    placement.video.mimes = deepAccess(bid, 'mediaTypes.video.mimes') || deepAccess(bid, 'params.video.mimes') || SUPPORTED_VIDEO_MIMES
    placement.video.playbackmethod = deepAccess(bid, 'mediaTypes.video.playbackmethod') || deepAccess(bid, 'params.video.playbackmethod') || SUPPORTED_VIDEO_PLAYBACK_METHODS
    placement.video.skip = deepAccess(bid, 'mediaTypes.video.skip') || deepAccess(bid, 'params.video.skip') || 0
    if (placement.video.skip === 1 && (deepAccess(bid, 'mediaTypes.video.skipafter') || deepAccess(bid, 'params.video.skipafter'))) {
      placement.video.skipafter = deepAccess(bid, 'mediaTypes.video.skipafter') || deepAccess(bid, 'params.video.skipafter')
    }
  }

  placement.ext = {'pid': bid.params.placementId}

  if (bidderRequest.paapi?.enabled) {
    placement.ext.ae = bid?.ortb2Imp?.ext?.ae
  }

  return placement
}

function _buildResponse (bid, currency, ttl) {
  let resp = {
    requestId: bid.impid,
    cpm: bid.price,
    width: bid.w,
    height: bid.h,
    creativeId: bid.crid,
    netRevenue: false,
    ttl: ttl,
    currency: currency
  }

  resp.meta = {};
  if (bid.adomain && bid.adomain.length > 0) {
    resp.meta.advertiserDomains = bid.adomain;
  }

  if (bid.ext.type === 'video') {
    resp.vastXml = bid.adm
  } else {
    resp.ad = bid.adm
  }

  if (deepAccess(bid, 'dealid')) {
    resp.dealId = bid.dealid
  }

  return resp
}

export const spec = {
  code: BIDDER_CODE,
  gvlid: GVLID,
  supportedMediaTypes: ['banner', 'video'],

  isBidRequestValid: function (bid) {
    if (!deepAccess(bid, 'params.placementId')) {
      logWarn('placementId param is reqeuired.')
      return false
    }
    if (deepAccess(bid, 'mediaTypes.banner.sizes')) {
      let isValid = _validateSizes(bid.mediaTypes.banner.sizes, 'banner')
      if (!isValid) {
        logWarn('Bid sizes are invalid.')
      }
      return isValid
    } else if (deepAccess(bid, 'mediaTypes.video.playerSize')) {
      let isValid = _validateSizes(bid.mediaTypes.video.playerSize, 'video')
      if (!isValid) {
        logWarn('Bid sizes are invalid.')
      }
      return isValid
    } else if (deepAccess(bid, 'sizes')) {
      let isValid = _validateSizes(bid.sizes, 'old')
      if (!isValid) {
        logWarn('Bid sizes are invalid.')
      }
      return isValid
    } else {
      logWarn('Bid sizes are required.')
      return false
    }
  },
  buildRequests: function (validBidRequests, bidderRequest) {
    // check if validBidRequests is not empty
    if (validBidRequests.length > 0) {
      let requestBody = {}
      requestBody.imp = []
      requestBody.site = _getSiteObj(bidderRequest)
      requestBody.device = _getDeviceObj()
      requestBody.id = bidderRequest.bidderRequestId;
      requestBody.ext = {'ce': (storage.cookiesAreEnabled() ? 1 : 0)}

      // Attaching GDPR Consent Params
      if (bidderRequest && bidderRequest.gdprConsent) {
        requestBody.user = {
          ext: {
            consent: bidderRequest.gdprConsent.consentString
          }
        };

        requestBody.regs = {
          ext: {
            gdpr: (bidderRequest.gdprConsent.gdprApplies ? 1 : 0)
          }
        };
      }

      _each(validBidRequests, function (bid) {
        requestBody.imp.push(_buildBid(bid, bidderRequest))
      })
      // Return the server request
      return {
        'method': 'POST',
        'url': BID_URL,
        'data': JSON.stringify(requestBody)
      }
    }
  },
  interpretResponse: function (serverResponse, request) {
    const bidResponses = []

    // response is not empty (HTTP 204)
    if (!isEmpty(serverResponse.body)) {
      _each(serverResponse.body.seatbid, function (seatbid) {
        _each(seatbid.bid, function (bid) {
          bidResponses.push(_buildResponse(bid, serverResponse.body.cur, serverResponse.body.ext.ttl))
        })
      })
    }

    return bidResponses
  },
  getUserSyncs: function (syncOptions, serverResponses) {
    const syncs = []

    if (deepAccess(serverResponses[0], 'body.ext.usersync') && !isEmpty(serverResponses[0].body.ext.usersync)) {
      _each(serverResponses[0].body.ext.usersync, function (match) {
        if ((syncOptions.iframeEnabled && match.type === 'iframe') || (syncOptions.pixelEnabled && match.type === 'image')) {
          syncs.push({
            type: match.type,
            url: match.url
          })
        }
      })
    }

    return syncs
  }
}
registerBidder(spec)