prebid/Prebid.js

View on GitHub
modules/seedingAllianceBidAdapter.js

Summary

Maintainability
B
4 hrs
Test Coverage
// jshint esversion: 6, es3: false, node: true
'use strict';

import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, NATIVE} from '../src/mediaTypes.js';
import {generateUUID, deepSetValue, isEmpty, replaceAuctionPrice} from '../src/utils.js';
import {config} from '../src/config.js';
import {getStorageManager} from '../src/storageManager.js';
import {ortbConverter} from '../libraries/ortbConverter/converter.js';

const GVL_ID = 371;
const BIDDER_CODE = 'seedingAlliance';
const DEFAULT_CUR = 'EUR';
const ENDPOINT_URL = 'https://b.nativendo.de/cds/rtb/bid?format=openrtb2.5&ssp=pb';
const NATIVENDO_KEY = 'nativendo_id';

export const storage = getStorageManager({bidderCode: BIDDER_CODE});

const converter = ortbConverter({
  context: {
    ttl: 360,
    netRevenue: true
  },
  request(buildRequest, imps, bidderRequest, context) {
    const request = buildRequest(imps, bidderRequest, context);
    // set basic page, this might be updated later by adunit param
    deepSetValue(request, 'site.page', bidderRequest.refererInfo.page);
    deepSetValue(request, 'regs.ext.pb_ver', '$prebid.version$');
    deepSetValue(request, 'cur', [config.getConfig('currency') || DEFAULT_CUR]);

    // As this is client side, we get needed info from headers
    delete request.device;

    return request;
  },
  imp(buildImp, bidRequest, context) {
    const imp = buildImp(bidRequest, context);
    // add tagid from params
    imp.tagid = bidRequest.params.adUnitId;

    return imp;
  }
});

export const spec = {
  code: BIDDER_CODE,
  gvlid: GVL_ID,
  supportedMediaTypes: [NATIVE, BANNER],

  isBidRequestValid: function (bid) {
    return !!bid.params.adUnitId;
  },

  buildRequests: (validBidRequests = [], bidderRequest) => {
    const oRtbRequest = converter.toORTB({bidRequests: validBidRequests, bidderRequest});
    let eids = getEids(validBidRequests[0]);

    // check for url in params and set in site object
    validBidRequests.forEach(bidRequest => {
      if (bidRequest.params.url) {
        deepSetValue(oRtbRequest, 'site.page', bidRequest.params.url);
      }
    });

    if (bidderRequest.gdprConsent) {
      oRtbRequest.user = {};

      deepSetValue(oRtbRequest, 'user.ext.consent', bidderRequest.gdprConsent.consentString);
      deepSetValue(oRtbRequest, 'regs.ext.gdpr', (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean' && bidderRequest.gdprConsent.gdprApplies) ? 1 : 0);
      deepSetValue(oRtbRequest, 'user.ext.eids', eids);
    }

    let endpoint = config.getConfig('seedingAlliance.endpoint') || ENDPOINT_URL;

    return {
      method: 'POST',
      url: endpoint,
      data: JSON.stringify(oRtbRequest),
      bidRequests: validBidRequests
    };
  },

  interpretResponse: function (serverResponse, { bidRequests }) {
    if (isEmpty(serverResponse.body)) {
      return [];
    }

    const { seatbid, cur } = serverResponse.body;

    const bidResponses = (typeof seatbid != 'undefined') ? flatten(seatbid.map(seat => seat.bid)).reduce((result, bid) => {
      result[bid.impid] = bid;
      return result;
    }, []) : [];
    return bidRequests
      .map((bidRequest) => {
        const bidId = bidRequest.bidId;
        const bidResponse = bidResponses[bidId];
        const type = bidRequest.nativeParams ? NATIVE : BANNER;

        if (bidResponse) {
          const bidObject = {
            requestId: bidRequest.bidId, // TODO get this value from response?
            cpm: bidResponse.price,
            creativeId: bidResponse.crid,
            ttl: 600,
            netRevenue: true,
            currency: cur,
            mediaType: type,
            meta: {
              advertiserDomains: bidResponse.adomain && bidResponse.adomain.length > 0 ? bidResponse.adomain : []
            }
          };

          if (type === NATIVE) {
            bidObject.native = parseNative(bidResponse, bidRequest.nativeParams);
            bidObject.mediaType = NATIVE;
          }

          if (type === BANNER) {
            bidObject.ad = replaceAuctionPrice(bidResponse.adm, bidResponse.price);
            bidObject.width = bidResponse.w;
            bidObject.height = bidResponse.h;
            bidObject.mediaType = BANNER;
          }

          return bidObject;
        }
      })
      .filter(Boolean);
  }
};

const getNativendoID = () => {
  let nativendoID = storage.localStorageIsEnabled() &&
      storage.getDataFromLocalStorage(NATIVENDO_KEY);

  if (!nativendoID) {
    if (storage.localStorageIsEnabled()) {
      nativendoID = generateUUID();
      storage.setDataInLocalStorage(NATIVENDO_KEY, nativendoID);
    }
  }

  return nativendoID;
}

const getEids = (bidRequest) => {
  const eids = [];
  const nativendoID = getNativendoID();

  if (nativendoID) {
    const nativendoUserEid = {
      source: 'nativendo.de',
      uids: [
        {
          id: nativendoID,
          atype: 1
        }
      ]
    };

    eids.push(nativendoUserEid);
  }

  if (bidRequest.userIdAsEids) {
    eids.push(bidRequest.userIdAsEids);
  }

  return eids;
}

function flatten(arr) {
  return [].concat(...arr);
}

function parseNative(bid, nativeParams) {
  let native;
  if (typeof bid.adm === 'string') {
    try {
      native = JSON.parse(bid.adm).native;
    } catch (e) {
      return;
    }
  } else {
    native = bid.adm.native;
  }

  const { assets, link, imptrackers } = native;

  let clickUrl = link.url.replace(/\$\{AUCTION_PRICE\}/g, bid.price);

  if (link.clicktrackers) {
    link.clicktrackers.forEach(function (clicktracker, index) {
      link.clicktrackers[index] = clicktracker.replace(/\$\{AUCTION_PRICE\}/g, bid.price);
    });
  }

  if (imptrackers) {
    imptrackers.forEach(function (imptracker, index) {
      imptrackers[index] = imptracker.replace(/\$\{AUCTION_PRICE\}/g, bid.price);
    });
  }

  const result = {
    url: clickUrl,
    clickUrl: clickUrl,
    clickTrackers: link.clicktrackers || undefined,
    impressionTrackers: imptrackers || undefined
  };

  let nativeParamKeys = Object.keys(nativeParams);
  let id = 0;

  nativeParamKeys.forEach(nativeParam => {
    assets.forEach(asset => {
      if (asset.id == id) {
        switch (nativeParam) {
          case 'title':
            result.title = asset.title.text;
            break;
          case 'body':
          case 'cta':
          case 'sponsoredBy':
            result[nativeParam] = asset.data.value;
            break;
          case 'image':
          case 'icon':
            result[nativeParam] = {
              url: asset.img.url,
              width: asset.img.w,
              height: asset.img.h
            };
            break;
        }
      }
    });

    id++;
  });

  return result;
}

registerBidder(spec);