prebid/Prebid.js

View on GitHub
modules/resetdigitalBidAdapter.js

Summary

Maintainability
D
1 day
Test Coverage
import { timestamp, deepAccess, isStr, deepClone } from '../src/utils.js';
import { getOrigin } from '../libraries/getOrigin/index.js';
import { config } from '../src/config.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER } from '../src/mediaTypes.js';

const BIDDER_CODE = 'resetdigital';
const CURRENCY = 'USD';

export const spec = {
  code: BIDDER_CODE,
  supportedMediaTypes: ['banner', 'video'],
  isBidRequestValid: function (bid) {
    return !!(bid.params.pubId || bid.params.zoneId);
  },
  buildRequests: function (validBidRequests, bidderRequest) {
    let stack =
      bidderRequest.refererInfo && bidderRequest.refererInfo.stack
        ? bidderRequest.refererInfo.stack
        : [];

    let spb =
      config.getConfig('userSync') &&
      config.getConfig('userSync').syncsPerBidder
        ? config.getConfig('userSync').syncsPerBidder
        : 5;

    const payload = {
      start_time: timestamp(),
      language: window.navigator.userLanguage || window.navigator.language,
      site: {
        domain: getOrigin(),
        iframe: !bidderRequest.refererInfo.reachedTop,
        // TODO: the last element in refererInfo.stack is window.location.href, that's unlikely to have been the intent here
        url: stack && stack.length > 0 ? [stack.length - 1] : null,
        https: window.location.protocol === 'https:',
        // TODO: is 'page' the right value here?
        referrer: bidderRequest.refererInfo.page,
      },
      imps: [],
      user_ids: validBidRequests[0].userId,
      sync_limit: spb,
    };

    if (bidderRequest && bidderRequest.gdprConsent) {
      payload.gdpr = {
        applies: bidderRequest.gdprConsent.gdprApplies,
        consent: bidderRequest.gdprConsent.consentString,
      };
    }

    if (bidderRequest && bidderRequest.uspConsent) {
      payload.ccpa = bidderRequest.uspConsent;
    }

    function getOrtb2Keywords(ortb2Obj) {
      const fields = [
        'site.keywords',
        'site.content.keywords',
        'user.keywords',
        'app.keywords',
        'app.content.keywords',
      ];
      let result = [];

      fields.forEach((path) => {
        let keyStr = deepAccess(ortb2Obj, path);
        if (isStr(keyStr)) result.push(keyStr);
      });
      return result;
    }

    // get the ortb2 keywords data (if it exists)
    let ortb2 = deepClone(bidderRequest && bidderRequest.ortb2);
    let ortb2KeywordsList = getOrtb2Keywords(ortb2);
    // get meta keywords data (if it exists)
    let metaKeywords = document.getElementsByTagName('meta')['keywords'];
    if (metaKeywords && metaKeywords.content) {
      metaKeywords = metaKeywords.content.split(',');
    }

    for (let x = 0; x < validBidRequests.length; x++) {
      let req = validBidRequests[x];

      let bidFloor = req.params.bidFloor ? req.params.bidFloor : null;
      let bidFloorCur = req.params.bidFloor ? req.params.bidFloorCur : null;

      if (typeof req.getFloor === 'function') {
        const floorInfo = req.getFloor({
          currency: CURRENCY,
          mediaType: BANNER,
          size: '*',
        });
        if (
          typeof floorInfo === 'object' &&
          floorInfo.currency === CURRENCY &&
          !isNaN(parseFloat(floorInfo.floor))
        ) {
          bidFloor = parseFloat(floorInfo.floor);
          bidFloorCur = CURRENCY;
        }
      }

      // get param kewords (if it exists)
      let paramsKeywords = req.params.keywords
        ? req.params.keywords.split(',')
        : [];
      // merge all keywords
      let keywords = ortb2KeywordsList
        .concat(paramsKeywords)
        .concat(metaKeywords);

      payload.imps.push({
        pub_id: req.params.pubId,
        site_id: req.params.siteID ? req.params.siteID : null,
        placement_id: req.params.placement ? req.params.placement : null,
        position: req.params.position ? req.params.position : null,
        bid_floor: bidFloor,
        bid_floor_cur: bidFloorCur,
        lat_long: req.params.latLong ? req.params.latLong : null,
        inventory: req.params.inventory ? req.params.inventory : null,
        visitor: req.params.visitor ? req.params.visitor : null,
        keywords: keywords.join(','),
        zone_id: req.params.zoneId,
        bid_id: req.bidId,
        // TODO: fix transactionId leak: https://github.com/prebid/Prebid.js/issues/9781
        imp_id: req.transactionId,
        sizes: req.sizes,
        force_bid: req.params.forceBid,
        coppa: config.getConfig('coppa') === true ? 1 : 0,
        media_types: deepAccess(req, 'mediaTypes'),
      });
    }

    let params = validBidRequests[0].params;
    let url = params.endpoint ? params.endpoint : '//ads.resetsrv.com';
    return {
      method: 'POST',
      url: url,
      data: JSON.stringify(payload),
      bids: validBidRequests,
    };
  },
  interpretResponse: function (serverResponse, bidRequest) {
    const bidResponses = [];
    if (!serverResponse || !serverResponse.body) {
      return bidResponses;
    }

    let res = serverResponse.body;
    if (!res.bids || !res.bids.length) {
      return [];
    }

    for (let x = 0; x < serverResponse.body.bids.length; x++) {
      let bid = serverResponse.body.bids[x];

      bidResponses.push({
        requestId: bid.bid_id,
        cpm: bid.cpm,
        width: bid.w,
        height: bid.h,
        ad: bid.html,
        vastUrl: bid.vast_url,
        vastXml: bid.vast_xml,
        mediaType: bid.html ? 'banner' : 'video',
        ttl: 120,
        creativeId: bid.crid,
        dealId: bid.deal_id,
        netRevenue: true,
        currency: 'USD',
        meta: {
          advertiserDomains: bid.adomain,
        },
      });
    }

    return bidResponses;
  },
  getUserSyncs: function (syncOptions, serverResponses, gdprConsent) {
    let syncs = [];
    if (!serverResponses.length || !serverResponses[0].body) {
      return syncs;
    }

    let gdprParams = '';
    if (gdprConsent) {
      if (typeof gdprConsent.gdprApplies === 'boolean') {
        gdprParams = `gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${
          gdprConsent.consentString
        }`;
      } else {
        gdprParams = `gdpr_consent=${gdprConsent.consentString}`;
      }
    }

    if (syncOptions.iframeEnabled) {
      syncs.push({
        type: 'iframe',
        url: `https://async.resetdigital.co/async_usersync.html?${gdprParams}`,
      });
    } else if (syncOptions.pixelEnabled) {
      syncs.push({
        type: 'image',
        url: `https://meta.resetdigital.co/pchain${
          gdprParams ? `?${gdprParams}` : ''
        }`,
      });
    }
    return syncs;
  },
};

registerBidder(spec);