prebid/Prebid.js

View on GitHub
modules/deltaprojectsBidAdapter.js

Summary

Maintainability
B
5 hrs
Test Coverage
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER} from '../src/mediaTypes.js';
import {
  _each,
  _map,
  createTrackPixelHtml,
  deepAccess,
  isFn,
  isNumber,
  logError,
  logWarn,
  setOnAny
} from '../src/utils.js';
import {config} from '../src/config.js';

export const BIDDER_CODE = 'deltaprojects';
export const BIDDER_ENDPOINT_URL = 'https://d5p.de17a.com/dogfight/prebid';
export const USERSYNC_URL = 'https://userservice.de17a.com/getuid/prebid';

/** -- isBidRequestValid -- */
function isBidRequestValid(bid) {
  if (!bid) return false;

  if (bid.bidder !== BIDDER_CODE) return false;

  // publisher id is required
  const publisherId = deepAccess(bid, 'params.publisherId')
  if (!publisherId) {
    logError('Invalid bid request, missing publisher id in params');
    return false;
  }

  return true;
}

/** -- Build requests -- */
function buildRequests(validBidRequests, bidderRequest) {
  /** == shared == */
  // -- build id
  const id = bidderRequest.bidderRequestId;

  // -- build site
  const publisherId = setOnAny(validBidRequests, 'params.publisherId');
  const siteId = setOnAny(validBidRequests, 'params.siteId');
  const site = {
    id: siteId,
    domain: bidderRequest.refererInfo.domain,
    page: bidderRequest.refererInfo.page,
    ref: bidderRequest.refererInfo.ref,
    publisher: { id: publisherId },
  };

  // -- build device
  const ua = navigator.userAgent;
  const device = {
    ua,
    w: screen.width,
    h: screen.height
  }

  // -- build user, reg
  let user = { ext: {} };
  const regs = { ext: {} };
  const gdprConsent = bidderRequest && bidderRequest.gdprConsent;
  if (gdprConsent) {
    user.ext = { consent: gdprConsent.consentString };
    if (typeof gdprConsent.gdprApplies == 'boolean') {
      regs.ext.gdpr = gdprConsent.gdprApplies ? 1 : 0
    }
  }

  // -- build tmax
  let tmax = (bidderRequest && bidderRequest.timeout > 0) ? bidderRequest.timeout : undefined;

  // build bid specific
  return validBidRequests.map(validBidRequest => {
    const openRTBRequest = buildOpenRTBRequest(validBidRequest, id, site, device, user, tmax, regs);
    return {
      method: 'POST',
      url: BIDDER_ENDPOINT_URL,
      data: openRTBRequest,
      options: { contentType: 'application/json' },
      bids: [validBidRequest],
    };
  });
}

function buildOpenRTBRequest(validBidRequest, id, site, device, user, tmax, regs) {
  // build cur
  const currency = config.getConfig('currency.adServerCurrency') || deepAccess(validBidRequest, 'params.currency');
  const cur = currency && [currency];

  // build impression
  const impression = buildImpression(validBidRequest, currency);

  // build test
  const test = deepAccess(validBidRequest, 'params.test') ? 1 : 0

  const at = 1

  // build source
  const source = {
    tid: validBidRequest.auctionId,
    fd: 1,
  }

  return {
    id,
    at,
    imp: [impression],
    site,
    device,
    user,
    test,
    tmax,
    cur,
    source,
    regs,
    ext: {},
  };
}

function buildImpression(bid, currency) {
  const impression = {
    id: bid.bidId,
    tagid: bid.params.tagId,
    ext: {},
  };

  const bannerMediaType = deepAccess(bid, `mediaTypes.${BANNER}`);
  impression.banner = buildImpressionBanner(bid, bannerMediaType);

  // bid floor
  const bidFloor = getBidFloor(bid, BANNER, '*', currency);
  if (bidFloor) {
    impression.bidfloor = bidFloor.floor;
    impression.bidfloorcur = bidFloor.currency;
  }

  return impression;
}

function buildImpressionBanner(bid, bannerMediaType) {
  const bannerSizes = (bannerMediaType && bannerMediaType.sizes) || bid.sizes;
  return {
    format: _map(bannerSizes, ([width, height]) => ({ w: width, h: height })),
  };
}

/** -- Interpret response -- */
function interpretResponse(serverResponse) {
  if (!serverResponse.body) {
    logWarn('Response body is invalid, return !!');
    return [];
  }

  const { body: { id, seatbid, cur } } = serverResponse;
  if (!id || !seatbid) {
    logWarn('Id / seatbid of response is invalid, return !!');
    return [];
  }

  const bidResponses = [];

  _each(seatbid, seatbid => {
    _each(seatbid.bid, bid => {
      const bidObj = {
        requestId: bid.impid,
        cpm: parseFloat(bid.price),
        width: parseInt(bid.w),
        height: parseInt(bid.h),
        creativeId: bid.crid || bid.id,
        dealId: bid.dealid || null,
        currency: cur,
        netRevenue: true,
        ttl: 60,
      };

      bidObj.mediaType = BANNER;
      bidObj.ad = bid.adm;
      if (bid.nurl) {
        bidObj.ad += createTrackPixelHtml(decodeURIComponent(bid.nurl));
      }
      if (bid.ext) {
        bidObj[BIDDER_CODE] = bid.ext;
      }
      bidResponses.push(bidObj);
    });
  });
  return bidResponses;
}

/** -- On Bid Won -- */
function onBidWon(bid) {
  let cpm = bid.cpm;
  if (bid.currency && bid.currency !== bid.originalCurrency && typeof bid.getCpmInNewCurrency === 'function') {
    cpm = bid.getCpmInNewCurrency(bid.originalCurrency);
  }
  const wonPrice = Math.round(cpm * 1000000);
  const wonPriceMacroPatten = /\$\{AUCTION_PRICE:B64\}/g;
  bid.ad = bid.ad.replace(wonPriceMacroPatten, wonPrice);
}

/** -- Get user syncs -- */
function getUserSyncs(syncOptions, serverResponses, gdprConsent) {
  const syncs = []

  if (syncOptions.pixelEnabled) {
    let gdprParams;
    if (gdprConsent) {
      if (typeof gdprConsent.gdprApplies === 'boolean') {
        gdprParams = `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`;
      } else {
        gdprParams = `?gdpr_consent=${gdprConsent.consentString}`;
      }
    } else {
      gdprParams = '';
    }
    syncs.push({
      type: 'image',
      url: USERSYNC_URL + gdprParams
    });
  }
  return syncs;
}

/** -- Get bid floor -- */
export function getBidFloor(bid, mediaType, size, currency) {
  if (isFn(bid.getFloor)) {
    const bidFloorCurrency = currency || 'USD';
    const bidFloor = bid.getFloor({currency: bidFloorCurrency, mediaType: mediaType, size: size});
    if (isNumber(bidFloor.floor)) {
      return bidFloor;
    }
  }
}

/** -- Register -- */
export const spec = {
  code: BIDDER_CODE,
  supportedMediaTypes: [BANNER],
  isBidRequestValid,
  buildRequests,
  interpretResponse,
  onBidWon,
  getUserSyncs,
};

registerBidder(spec);