Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor of Kargo Prebid adapter. #11

Merged
merged 29 commits into from
Feb 9, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5093cfd
pageURL pull from topmostLocation
jsadwith Oct 25, 2022
0bdf068
Merge branch 'prebid:master' into master
jsadwith Nov 1, 2022
1bec84d
Kargo: Support for client hints (#9)
jsadwith Nov 1, 2022
9e83a91
Adding tests for sua
jsadwith Nov 2, 2022
c050208
Merge branch 'prebid:master' into master
jsadwith Nov 30, 2022
38f9fb7
Kargo: Update referer logic
jsadwith Nov 30, 2022
78c6096
Refactor of Kargo Prebid adapter.
njflynn Dec 6, 2022
7cc5bf9
PR comments addressed.
njflynn Dec 9, 2022
5760b22
Merge branch 'master' into KRKPD-138
jsadwith Dec 9, 2022
30862a6
Feedback addressed.
njflynn Dec 12, 2022
3991d54
Merge branch 'KRKPD-138' of github.com:KargoGlobal/Prebid.js into KRK…
njflynn Dec 12, 2022
ced317e
Pr comments addressed.
njflynn Dec 12, 2022
805dfd4
Continuing refactor of Kargo Bid adapter.
njflynn Dec 13, 2022
36272e4
Logic adjustment to exclude values when not present. Relying on serve…
njflynn Dec 20, 2022
f26af68
Updating unit tests.
njflynn Jan 6, 2023
e90522d
PR feedback addressed.
njflynn Jan 9, 2023
5b6e1b9
Refactoring bid adapter functions.
njflynn Jan 10, 2023
8aad772
PR feedback addressed.
njflynn Jan 10, 2023
d4119da
Additional refactoring.
njflynn Jan 11, 2023
0541d1f
Refactoring for each to use Object entries.
njflynn Jan 12, 2023
50214ed
Minor fixes.
njflynn Jan 13, 2023
28841cc
Minor fixes.
njflynn Jan 13, 2023
215005e
Minor fixes.
njflynn Jan 16, 2023
b6fc868
TDID and linting updates
jsadwith Jan 17, 2023
1bae7d0
Merge branch 'master' of github.com:KargoGlobal/Prebid.js
njflynn Jan 20, 2023
a4546a2
Fixing unit test.
njflynn Jan 20, 2023
dcf378e
Merge branch 'master' of github.com:KargoGlobal/Prebid.js
njflynn Feb 9, 2023
da8440f
Conflicts resolved with master.
njflynn Feb 9, 2023
28167dd
Conflicts resolved with master.
njflynn Feb 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 114 additions & 37 deletions modules/kargoBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getStorageManager } from '../src/storageManager.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';

const BIDDER_CODE = 'kargo';
const HOST = 'https://krk.kargo.com';
const HOST = 'https://krk2.kargo.com';
const SYNC = 'https://crb.kargo.com/api/v1/initsyncrnd/{UUID}?seed={SEED}&idx={INDEX}&gdpr={GDPR}&gdpr_consent={GDPR_CONSENT}&us_privacy={US_PRIVACY}';
const SYNC_COUNT = 5;
const GVLID = 972;
Expand All @@ -28,57 +28,77 @@ export const spec = {
},
buildRequests: function(validBidRequests, bidderRequest) {
const currencyObj = config.getConfig('currency');
const currency = (currencyObj && currencyObj.adServerCurrency) || 'USD';
const bidIDs = {};
const bidSizes = {};
const currency = (currencyObj && currencyObj.adServerCurrency);
njflynn marked this conversation as resolved.
Show resolved Hide resolved
const impressions = [];

_each(validBidRequests, bid => {
bidIDs[bid.bidId] = bid.params.placementId;
bidSizes[bid.bidId] = bid.sizes;
impressions.push(spec.getImpression(bid))
});

const firstBidRequest = validBidRequests[0];

const tdid = deepAccess(firstBidRequest, 'userId.tdid')

const transformedParams = Object.assign({}, {
sessionId: spec._getSessionId(),
requestCount: spec._getRequestCount(),
timeout: bidderRequest.timeout,
currency,
cpmGranularity: 1,
timestamp: (new Date()).getTime(),
cpmRange: {
floor: 0,
ceil: 20
},
bidIDs,
bidSizes,
// Pull Social Canvas segments and embed URL
const socialCanvas = deepAccess(firstBidRequest, 'params.socialCanvas');

const krakenParams = Object.assign({}, {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Things are looking good, to bring it one step further, what do you think about extracting the functions into individual function blocks, and then tie it altogether at the end into the exported spec object? i.e (excerpt):

function isBidRequestValid(bid) {
  if (!bid || !bid.params) {
    return false;
  }

  return !!bid.params.placementId;
};

function generateUUID() {
  try {
    // crypto.getRandomValues is supported everywhere but Opera Mini for years
    var buffer = new Uint8Array(16);
    crypto.getRandomValues(buffer);
    buffer[6] = (buffer[6] & ~176) | 64;
    buffer[8] = (buffer[8] & ~64) | 128;
    var hex = Array.prototype.map.call(new Uint8Array(buffer), function(x) {
      return ('00' + x.toString(16)).slice(-2);
    }).join('');
    return hex.slice(0, 8) + '-' + hex.slice(8, 12) + '-' + hex.slice(12, 16) + '-' + hex.slice(16, 20) + '-' + hex.slice(20);
  } catch (e) {
    return '';
  }
};

function getSessionID() {
  if (!sessionId) {
    return generateUUID();
  }

  return sessionId;
};

export const spec = {
  gvlid: GVLID,
  code: BIDDER_CODE,
  isBidRequestValid,
  generateUUID,
  getSessionID,
};

registerBidder(spec);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've pulled all function declarations out of the spec object. Let me know what you think

pbv: '$prebid.version$',
aid: firstBidRequest.auctionId,
sid: spec._getSessionId(),
url: spec._getAllMetadata(bidderRequest).pageURL,
to: bidderRequest.timeout,
ts: new Date().getTime(),
device: {
width: window.screen.width,
height: window.screen.height
size: {
width: window.screen.width,
height: window.screen.height
}
njflynn marked this conversation as resolved.
Show resolved Hide resolved
},
imp: impressions,
socan: socialCanvas,
jsadwith marked this conversation as resolved.
Show resolved Hide resolved
njflynn marked this conversation as resolved.
Show resolved Hide resolved
page: {
id: spec._getLocalStorageSafely['pageViewId'],
timestamp: spec._getLocalStorageSafely['pageViewTimestamp'],
url: spec._getLocalStorageSafely['pageViewUrl']
},
prebidRawBidRequests: validBidRequests
}, spec._getAllMetadata(bidderRequest, tdid));
user: spec._getUserIds(tdid, bidderRequest.uspConsent, bidderRequest.gdprConsent),
eids: firstBidRequest.userIdAsEids

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should eids be nested within user?

"user": {
        "kid": "9449b364-1e40-ff76-a2f1-e9a3caafe1c0", // Kargo ID 
        "cid": "de979bd7-73b5-4785-913e-baf079872c20", // Kargo client ID
        "crbids": {}, // Cerberus IDs
        "tdid": "ed1562d5-e52b-406f-8e65-e5ab3ed5583c", // Trade Desk ID
        "usp": "1---",
        "gdpr": {
            "consent": "",
            "applies": false
        },
        "eids": { // 3rd party user info from userIdAsEids
            [ 
                {

Ref: https://kargo1.atlassian.net/wiki/spaces/KRAK/pages/3171876871/Prebid.js+Adapter+-+Request+Format+Proposal

});

const reqCount = spec._getRequestCount()
if(reqCount != null && reqCount > 0) {
krakenParams.requestCount = spec._getRequestCount()
}

if(currency != null && currency != "USD") {
njflynn marked this conversation as resolved.
Show resolved Hide resolved
krakenParams.cur = currency
}

// User Agent Client Hints / SUA
const uaClientHints = deepAccess(firstBidRequest, 'ortb2.device.sua');
if (uaClientHints) {
transformedParams.device.sua = pick(uaClientHints, ['browsers', 'platform', 'mobile', 'model']);
}

// Pull Social Canvas segments and embed URL
const socialCanvas = deepAccess(firstBidRequest, 'params.socialCanvas');
if (socialCanvas) {
transformedParams.socialCanvasSegments = socialCanvas.segments;
transformedParams.socialEmbedURL = socialCanvas.embedURL;
suaAttributes = ['browsers', 'platform', 'mobile', 'model', 'source']
suaValidAttributes = []

suaAttributes.forEach(attributeKey => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thoughts on doing something like this to improve readability?

      for (const suaKey of suaAttributes) {
        const suaValue = uaClientHints[suaKey]
        if (!suaValue) {
          continue
        }
        if (typeof suaValue !== 'string') {
          continue
        }
        if (suaValue.trim() === '') {
          continue
        }

        switch (suaKey) {
          case 'mobile', 'source':
            if (suaValue.length < 1) {
              return;
            };
          default:
            suaValidAttributes.push(suaKey);
        }
      }

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented a slightly modified version of this as not all values are strings.

var attributeValue = uaClientHints[attributeKey]
if(attributeValue != null) {
if(((attributeKey == 'mobile' || attributeKey == 'source') && attributeValue < 1) ||
(attributeKey == 'model' && attributeValue.trim() == '')) {
return
}
suaValidAttributes.push(attributeKey)
}
})

krakenParams.device.sua = pick(uaClientHints, suaValidAttributes);
}

const encodedParams = encodeURIComponent(JSON.stringify(transformedParams));
return Object.assign({}, bidderRequest, {
method: 'GET',
url: `${HOST}/api/v2/bid`,
data: `json=${encodedParams}`,
method: 'POST',
url: `${HOST}/api/v1/prebid`,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: probably not necessary but should the entire route just be a const?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the latest changes I've added a "HOST" and "REQUEST_ENDPOINT" consts as part of a BIDDER object. That would allow for future reusability of both individually, should the need arise.

data: krakenParams,
currency: currency
});
},
Expand Down Expand Up @@ -228,10 +248,10 @@ export const spec = {
return crb.clientId;
},

_getAllMetadata(bidderRequest, tdid) {
_getAllMetadata(bidderRequest) {
return {
userIDs: spec._getUserIds(tdid, bidderRequest.uspConsent, bidderRequest.gdprConsent),
pageURL: bidderRequest?.refererInfo?.topmostLocation || bidderRequest?.refererInfo?.page,
pageURL: bidderRequest?.refererInfo?.page,
rawCRB: storage.getCookie('krg_crb'),
rawCRBLocalStorage: spec._getLocalStorageSafely('krg_crb')
};
Expand Down Expand Up @@ -284,6 +304,63 @@ export const spec = {

triggerPixel(timeoutRequestUrl);
} catch (e) {}
},

getImpression(bid) {
const imp = {
id: bid.bidId,
tid: bid.transactionId,
pid: bid.params.placementId,
code: bid.adUnitCode,
banner: bid.mediaTypes.banner,
video: bid.mediaTypes.video,
fpd: {
gpid: getGPID(bid)
},
}

if(bid.floor > 0) {
imp.floor = bid.floor
}

if(bid.bidRequestsCount > 0) {
imp.bidRequestCount = bid.bidRequestsCount
}

if(bid.bidderRequestsCount > 0) {
imp.bidderRequestCount = bid.bidderRequestsCount
}

if(bid.bidderWinsCount > 0) {
imp.bidderWinCount = bid.bidderWinsCount
}
return imp
},

getGPID(bid) {
if(bid.ortb2Imp.gpid != null & bid.ortb2Imp.gpid != "") {
return bid.ortb2Imp.gpid
}

if (bid.ortb2Imp.Extensions.Data.PBadSlot != null && bid.ortb2Imp.Extensions.Data.PBadSlot != "") {
return bid.ortb2Imp.Extensions.Data.PBadSlot
}

if (bid.ortb2Imp.Extensions.Data.PbAdSlot != null && bid.ortb2Imp.Extensions.Data.PbAdSlot != "") {
return bid.ortb2Imp.Extensions.Data.PbAdSlot
}

if (bid.ortb2Imp.Extensions.Data.AdServer.AdSlot != null && bid.ortb2Imp.Extensions.Data.AdServer.AdSlot != "") {
return bid.ortb2Imp.Extensions.Data.AdServer.AdSlot
}

if (bid.AdUnitCode != null && bid.AdUnitCode != "") {
return prebidRequest.AdUnitCode
}

return ""
}

};

registerBidder(spec);
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 26 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,26 @@
"@wdio/sync": "~7.5.2",
"ajv": "6.12.3",
"assert": "^2.0.0",
"babel-loader": "^8.0.5",
"babel-loader": "^8.3.0",
"babel-plugin-istanbul": "^6.1.1",
"babel-register": "^6.26.0",
"body-parser": "^1.19.0",
"chai": "^4.2.0",
"body-parser": "^1.20.1",
"chai": "^4.3.7",
"coveralls": "^3.1.0",
"deep-equal": "^2.0.3",
"documentation": "^14.0.0",
"es5-shim": "^4.5.14",
"deep-equal": "^2.1.0",
"documentation": "^3.0.4",
"es5-shim": "^4.6.7",
"eslint": "^7.27.0",
"eslint-config-standard": "^10.2.1",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prebid": "file:./plugins/eslint",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-standard": "^3.0.1",
"execa": "^1.0.0",
"faker": "^5.5.3",
"fs.extra": "^1.3.2",
"gulp": "^4.0.0",
"gulp": "^4.0.2",
"gulp-clean": "^0.4.0",
"gulp-concat": "^2.6.0",
"gulp-connect": "^5.7.0",
Expand All @@ -74,14 +74,14 @@
"gulp-shell": "^0.8.0",
"gulp-sourcemaps": "^3.0.0",
"gulp-terser": "^2.0.1",
"gulp-util": "^3.0.0",
"gulp-util": "^1.3.0",
"is-docker": "^2.2.1",
"istanbul": "^0.4.5",
"karma": "^6.3.2",
"karma": "^6.4.1",
"karma-babel-preprocessor": "^8.0.1",
"karma-browserstack-launcher": "1.4.0",
"karma-chai": "^0.1.0",
"karma-chrome-launcher": "^3.1.0",
"karma-chrome-launcher": "^3.1.1",
"karma-coverage": "^2.0.1",
"karma-coverage-istanbul-reporter": "^3.0.3",
"karma-es5-shim": "^0.0.4",
Expand All @@ -105,32 +105,35 @@
"through2": "^4.0.2",
"url": "^0.11.0",
"url-parse": "^1.0.5",
"video.js": "^7.17.0",
"video.js": "^7.20.3",
"videojs-contrib-ads": "^6.9.0",
"videojs-ima": "^1.11.0",
"videojs-playlist": "^5.0.0",
"webdriverio": "^7.6.1",
"webpack": "^5.70.0",
"webpack-bundle-analyzer": "^4.5.0",
"webdriverio": "^7.27.0",
"webpack": "^5.75.0",
"webpack-bundle-analyzer": "^4.7.0",
"webpack-manifest-plugin": "^5.0.0",
"webpack-stream": "^7.0.0",
"yargs": "^1.3.1"
},
"dependencies": {
"@babel/core": "^7.16.7",
"@babel/plugin-transform-runtime": "^7.18.9",
"@babel/preset-env": "^7.16.8",
"@babel/runtime": "^7.18.9",
"core-js": "^3.13.0",
"core-js-pure": "^3.13.0",
"@babel/core": "^7.20.5",
"@babel/plugin-transform-runtime": "^7.19.6",
"@babel/preset-env": "^7.20.2",
"@babel/runtime": "^7.20.6",
"core-js": "^3.26.1",
"core-js-pure": "^3.26.1",
"criteo-direct-rsa-validate": "^1.1.0",
"crypto-js": "^3.3.0",
"dlv": "1.1.3",
"dset": "3.1.2",
"express": "^4.15.4",
"express": "^4.18.2",
"fun-hooks": "^0.9.9",
"gulp-v4": "^4.0.0-alpha.5",
"gutil": "^1.6.4",
"just-clone": "^1.0.2",
"live-connect-js": "2.4.0"
"live-connect-js": "2.4.0",
"polyfill-object.fromentries": "^1.0.1"
},
"optionalDependencies": {
"fsevents": "^2.3.2"
Expand Down