prebid/Prebid.js

View on GitHub
modules/orbidderBidAdapter.js

Summary

Maintainability
B
5 hrs
Test Coverage
import { isFn, isPlainObject } from '../src/utils.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { getStorageManager } from '../src/storageManager.js';
import { BANNER, NATIVE } from '../src/mediaTypes.js';
import { convertOrtbRequestToProprietaryNative } from '../src/native.js';
import { getGlobal } from '../src/prebidGlobal.js';

/**
 * @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
 * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid
 */
const storageManager = getStorageManager({ bidderCode: 'orbidder' });

/**
 * Determines whether or not the given bid response is valid.
 *
 * @param {object} bidResponse The bid response to validate.
 * @return boolean True if this is a valid bid response, and false if it is not valid.
 */
function isBidResponseValid(bidResponse) {
  let requiredKeys = ['requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency'];

  switch (bidResponse.mediaType) {
    case BANNER:
      requiredKeys = requiredKeys.concat(['width', 'height', 'ad']);
      break;
    case NATIVE:
      if (!bidResponse.native.hasOwnProperty('impressionTrackers')) {
        return false
      }
      break;
    default:
      return false
  }

  for (const key of requiredKeys) {
    if (!bidResponse.hasOwnProperty(key)) {
      return false
    }
  }

  return true
}

export const spec = {
  code: 'orbidder',
  gvlid: 559,
  hostname: 'https://orbidder.otto.de',
  supportedMediaTypes: [BANNER, NATIVE],

  /**
   * Returns a customzied hostname if 'ov_orbidder_host' is set in the browser's local storage.
   * This is only used for integration testing.
   *
   * @return The hostname bid requests should be sent to.
   */
  getHostname() {
    let ret = this.hostname;
    try {
      ret = storageManager.getDataFromLocalStorage('ov_orbidder_host') || ret;
    } catch (e) {
    }
    return ret;
  },

  /**
   * Determines whether or not the given bid request is valid.
   *
   * @param {object} bid The bid to validate.
   * @return boolean True if this is a valid bid, and false if it is not valid.
   */
  isBidRequestValid(bid) {
    return !!(bid.sizes && bid.bidId && bid.params &&
      (bid.params.accountId && (typeof bid.params.accountId === 'string')) &&
      (bid.params.placementId && (typeof bid.params.placementId === 'string')) &&
      ((typeof bid.params.keyValues === 'undefined') || (typeof bid.params.keyValues === 'object')));
  },

  /**
   * Build a request from the list of valid BidRequests that will be sent by prebid to the orbidder /bid endpoint, i.e. the server.
   *
   * @param {BidRequest[]} validBidRequests A non-empty list of valid bid requests that should be sent to the orbidder /bid endpoint,
   * i.e. the server.
   * @return The requests for the orbidder /bid endpoint, i.e. the server.
   */
  buildRequests(validBidRequests, bidderRequest) {
    // convert Native ORTB definition to old-style prebid native definition
    validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests);

    const hostname = this.getHostname();
    return validBidRequests.map((bidRequest) => {
      let referer = '';
      if (bidderRequest && bidderRequest.refererInfo) {
        referer = bidderRequest.refererInfo.page || '';
      }

      bidRequest.params.bidfloor = getBidFloor(bidRequest);

      let httpReq = {
        url: `${hostname}/bid`,
        method: 'POST',
        options: { withCredentials: true },
        data: {
          v: getGlobal().version,
          pageUrl: referer,
          ...bidRequest // get all data provided by bid request
        }
      };

      if (bidderRequest && bidderRequest.gdprConsent) {
        httpReq.data.gdprConsent = {
          consentString: bidderRequest.gdprConsent.consentString,
          consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') && bidderRequest.gdprConsent.gdprApplies
        };
      }
      return httpReq;
    });
  },

  /**
   * Unpack the response from the orbidder /bid endpoint into a list of bids.
   *
   * @param {*} serverResponse A successful response from the orbidder /bid endpoint, i.e. the server.
   * @return {Bid[]} An array of bids from orbidder.
   */
  interpretResponse(serverResponse) {
    const bidResponses = [];
    serverResponse = serverResponse.body;
    if (serverResponse && (serverResponse.length > 0)) {
      serverResponse.forEach((bidResponse) => {
        if (isBidResponseValid(bidResponse)) {
          if (Array.isArray(bidResponse.advertiserDomains)) {
            bidResponse.meta = {
              advertiserDomains: bidResponse.advertiserDomains
            }
          }
          bidResponses.push(bidResponse);
        }
      });
    }
    return bidResponses;
  },
};

/**
 * Get bid floor from Price Floors Module
 * @param {Object} bid
 * @returns {float||undefined}
 */
function getBidFloor(bid) {
  if (!isFn(bid.getFloor)) {
    return bid.params.bidfloor;
  }

  const floor = bid.getFloor({
    currency: 'EUR',
    mediaType: '*',
    size: '*'
  });
  if (isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'EUR') {
    return floor.floor;
  }
  return undefined;
}

registerBidder(spec);