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

Glimpse: update api and request shape, optimize and refactor #8237

Merged
merged 1 commit into from
Apr 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
265 changes: 118 additions & 147 deletions modules/glimpseBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,230 +1,201 @@
import { BANNER } from '../src/mediaTypes.js'
import { config } from '../src/config.js'
import { getStorageManager } from '../src/storageManager.js'
import { isArray } from '../src/utils.js'
import { registerBidder } from '../src/adapters/bidderFactory.js'

const GVLID = 1012
const BIDDER_CODE = 'glimpse'
const storageManager = getStorageManager({bidderCode: BIDDER_CODE})
const ENDPOINT = 'https://api.glimpsevault.io/ads/serving/public/v1/prebid'
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { config } from '../src/config.js';
import { BANNER } from '../src/mediaTypes.js';
import { getStorageManager } from '../src/storageManager.js';
import {
isArray,
isEmpty,
isEmptyStr,
isStr,
isPlainObject,
} from '../src/utils.js';

const GVLID = 1012;
const BIDDER_CODE = 'glimpse';
const storageManager = getStorageManager({
gvlid: GVLID,
bidderCode: BIDDER_CODE,
});
const ENDPOINT = 'https://market.glimpsevault.io/public/v1/prebid';
const LOCAL_STORAGE_KEY = {
vault: {
jwt: 'gp_vault_jwt',
},
}
};

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

/**
* Determines whether or not the given bid request is valid
* Determines if the bid request is valid
* @param bid {BidRequest} The bid to validate
* @return {boolean}
*/
isBidRequestValid: (bid) => {
return (
hasValue(bid) &&
hasValue(bid.params) &&
hasStringValue(bid.params.placementId)
)
const pid = bid?.params?.pid;
return isStr(pid) && !isEmptyStr(pid);
},

/**
* Builds http request for Glimpse bids
* Builds the http request
* @param validBidRequests {BidRequest[]}
* @param bidderRequest {BidderRequest}
* @returns {ServerRequest}
*/
buildRequests: (validBidRequests, bidderRequest) => {
const auth = getVaultJwt()
const referer = getReferer(bidderRequest)
const gdprConsent = getGdprConsentChoice(bidderRequest)
const bidRequests = validBidRequests.map(processBidRequest)
const firstPartyData = getFirstPartyData()
const url = buildQuery(bidderRequest);
const auth = getVaultJwt();
const referer = getReferer(bidderRequest);
const imp = validBidRequests.map(processBidRequest);
const fpd = getFirstPartyData();

const data = {
auth,
data: {
referer,
gdprConsent,
bidRequests,
site: firstPartyData.site,
user: firstPartyData.user,
bidderCode: spec.code,
}
}
imp,
fpd,
},
};

return {
method: 'POST',
url: ENDPOINT,
url,
data: JSON.stringify(data),
options: {},
}
};
},

/**
* Parse response from Glimpse server
* @param bidResponse {ServerResponse}
* Parse http response
* @param response {ServerResponse}
* @returns {Bid[]}
*/
interpretResponse: (bidResponse) => {
const isValidResponse = isValidBidResponse(bidResponse)

if (isValidResponse) {
const {auth, data} = bidResponse.body
setVaultJwt(auth)
return data.bids
interpretResponse: (response) => {
if (isValidResponse(response)) {
const { auth, data } = response.body;
setVaultJwt(auth);
const bids = data.bids.map(processBidResponse);
return bids;
}

return []
return [];
},
}
};

function setVaultJwt(auth) {
storageManager.setDataInLocalStorage(LOCAL_STORAGE_KEY.vault.jwt, auth)
storageManager.setDataInLocalStorage(LOCAL_STORAGE_KEY.vault.jwt, auth);
}

function getVaultJwt() {
return storageManager.getDataFromLocalStorage(LOCAL_STORAGE_KEY.vault.jwt) || ''
return (
storageManager.getDataFromLocalStorage(LOCAL_STORAGE_KEY.vault.jwt) || ''
);
}

function getReferer(bidderRequest) {
const hasReferer =
hasValue(bidderRequest) &&
hasValue(bidderRequest.refererInfo) &&
hasStringValue(bidderRequest.refererInfo.referer)

if (hasReferer) {
return bidderRequest.refererInfo.referer
}

return ''
return bidderRequest?.refererInfo?.referer || '';
}

function getGdprConsentChoice(bidderRequest) {
const hasGdprConsent =
hasValue(bidderRequest) &&
hasValue(bidderRequest.gdprConsent)
function buildQuery(bidderRequest) {
let url = appendQueryParam(ENDPOINT, 'ver', '$prebid.version$');

if (hasGdprConsent) {
const gdprConsent = bidderRequest.gdprConsent
const hasGdprApplies = hasBooleanValue(gdprConsent.gdprApplies)
const timeout = config.getConfig('bidderTimeout');
url = appendQueryParam(url, 'tmax', timeout);

return {
consentString: gdprConsent.consentString || '',
vendorData: gdprConsent.vendorData || {},
gdprApplies: hasGdprApplies ? gdprConsent.gdprApplies : true,
}
if (gdprApplies(bidderRequest)) {
const consentString = bidderRequest.gdprConsent.consentString;
url = appendQueryParam(url, 'reg', 'gdpr');
url = appendQueryParam(url, 'cs', consentString);
} else if (ccpaApplies(bidderRequest)) {
msm0504 marked this conversation as resolved.
Show resolved Hide resolved
url = appendQueryParam(url, 'reg', 'ccpa');
url = appendQueryParam(url, 'cs', bidderRequest.uspConsent);
} else {
url = appendQueryParam(url, 'reg', 'none');
}
return url;
}

return {
consentString: '',
vendorData: {},
gdprApplies: false,
function appendQueryParam(url, key, value) {
if (!value) {
return url;
}
const prefix = url.includes('?') ? '&' : '?';
return `${url}${prefix}${key}=${encodeURIComponent(value)}`;
}

function gdprApplies(bidderRequest) {
return Boolean(bidderRequest?.gdprConsent?.gdprApplies);
}

function processBidRequest(bidRequest) {
const demand = bidRequest.params.demand || 'glimpse'
const sizes = normalizeSizes(bidRequest.sizes)
const keywords = bidRequest.params.keywords || {}
function ccpaApplies(bidderRequest) {
return (
!isEmptyStr(bidderRequest.uspConsent) &&
bidderRequest.uspConsent.substr(1, 3) !== '---'
);
}

function processBidRequest(bid) {
const sizes = normalizeSizes(bid.sizes);

return {
demand,
bid: bid.bidId,
pid: bid.params.pid,
sizes,
keywords,
bidId: bidRequest.bidId,
placementId: bidRequest.params.placementId,
unitCode: bidRequest.adUnitCode,
}
};
}

function normalizeSizes(sizes) {
const isSingleSize =
isArray(sizes) &&
sizes.length === 2 &&
!isArray(sizes[0]) &&
!isArray(sizes[1])
!isArray(sizes[1]);

if (isSingleSize) {
return [sizes]
return [sizes];
}

return sizes
return sizes;
}

function getFirstPartyData() {
const siteKeywords = parseGlobalKeywords('site')
const userKeywords = parseGlobalKeywords('user')

const siteAttributes = getConfig('ortb2.site.ext.data', {})
const userAttributes = getConfig('ortb2.user.ext.data', {})

return {
site: {
keywords: siteKeywords,
attributes: siteAttributes,
},
user: {
keywords: userKeywords,
attributes: userAttributes,
},
}
let fpd = config.getConfig('ortb2') || {};
optimizeObject(fpd);
return fpd;
}

function parseGlobalKeywords(scope) {
const keywords = getConfig(`ortb2.${scope}.keywords`, '')

return keywords
.split(', ')
.filter((keyword) => keyword !== '')
}

function getConfig(path, defaultValue) {
return config.getConfig(path) || defaultValue
}

function isValidBidResponse(bidResponse) {
return (
hasValue(bidResponse) &&
hasValue(bidResponse.body) &&
hasValue(bidResponse.body.data) &&
hasArrayValue(bidResponse.body.data.bids) &&
hasStringValue(bidResponse.body.auth)
)
}

function hasValue(value) {
return (
value !== undefined &&
value !== null
)
function optimizeObject(obj) {
if (!isPlainObject(obj)) {
return;
}
for (const [key, value] of Object.entries(obj)) {
optimizeObject(value);
// only delete empty object, array, or string
if (
(isPlainObject(value) || isArray(value) || isStr(value)) &&
isEmpty(value)
) {
delete obj[key];
}
}
}

function hasBooleanValue(value) {
return (
hasValue(value) &&
typeof value === 'boolean'
)
function isValidResponse(bidResponse) {
const auth = bidResponse?.body?.auth;
const bids = bidResponse?.body?.data?.bids;
return isStr(auth) && isArray(bids) && !isEmpty(bids);
}

function hasStringValue(value) {
return (
hasValue(value) &&
typeof value === 'string' &&
value.length > 0
)
}
function processBidResponse(bid) {
const meta = bid.meta || {};
meta.advertiserDomains = bid.meta?.advertiserDomains || [];

function hasArrayValue(value) {
return (
hasValue(value) &&
isArray(value) &&
value.length > 0
)
return {
...bid,
meta,
};
}

registerBidder(spec)
registerBidder(spec);
15 changes: 7 additions & 8 deletions modules/glimpseBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,14 @@ const adUnits = [
sizes: [[300, 250]],
},
},
bids: [{
bidder: 'glimpse',
params: {
placementId: 'e53a7f564f8f44cc913b',
keywords: {
country: 'uk',
bids: [
{
bidder: 'glimpse',
params: {
pid: 'e53a7f564f8f44cc913b',
},
},
}],
],
},
]
];
```
Loading