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

1plusX Rtd Module: Add FPID support for PAPI and Fix Test Bug #9115

Merged
merged 1 commit into from
Oct 17, 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
37 changes: 32 additions & 5 deletions modules/1plusXRtdProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const MODULE_NAME = '1plusX';
const ORTB2_NAME = '1plusX.com'
const PAPI_VERSION = 'v1.0';
const LOG_PREFIX = '[1plusX RTD Module]: ';
const OPE_FPID = 'ope_fpid'
const LEGACY_SITE_KEYWORDS_BIDDERS = ['appnexus'];
export const segtaxes = {
// cf. https://github.com/InteractiveAdvertisingBureau/openrtb/pull/108
Expand Down Expand Up @@ -57,6 +58,12 @@ export const extractConfig = (moduleConfig, reqBidsConfigObj) => {
return { customerId, timeout, bidders };
}

/**
* Extracts consent from the prebid consent object and translates it
* into a 1plusX profile api query parameter parameter dict
* @param {object} prebid gdpr object
* @returns dictionary of papi gdpr query parameters
*/
export const extractConsent = ({ gdpr }) => {
if (!gdpr) {
return null
Expand All @@ -65,22 +72,39 @@ export const extractConsent = ({ gdpr }) => {
if (!(gdprApplies == '0' || gdprApplies == '1')) {
throw 'TCF Consent: gdprApplies has wrong format'
}
if (!(typeof consentString === 'string')) {
throw 'TCF Consent: consentString is not string'
if (consentString && typeof consentString != 'string') {
throw 'TCF Consent: consentString must be string if defined'
}
const result = {
'gdpr_applies': gdprApplies,
'consent_string': consentString
}
return result
}

/**
* Extracts the OPE first party id field from local storage
* @returns fpid string if found, else null
*/
export const extractFpid = () => {
try {
const fpid = window.localStorage.getItem(OPE_FPID);
if (fpid) {
return fpid;
}
return null;
} catch (error) {
return null;
}
}
/**
* Gets the URL of Profile Api from which targeting data will be fetched
* @param {Object} config
* @param {string} config.customerId
* @param {object} consent query params as dict
* @param {string} oneplusx first party id (nullable)
* @returns {string} URL to access 1plusX Profile API
*/
export const getPapiUrl = (customerId, consent) => {
export const getPapiUrl = (customerId, consent, fpid) => {
// https://[yourClientId].profiles.tagger.opecloud.com/[VERSION]/targeting?url=
const currentUrl = encodeURIComponent(window.location.href);
var papiUrl = `https://${customerId}.profiles.tagger.opecloud.com/${PAPI_VERSION}/targeting?url=${currentUrl}`;
Expand All @@ -89,6 +113,9 @@ export const getPapiUrl = (customerId, consent) => {
papiUrl += `&${key}=${value}`
})
}
if (fpid) {
papiUrl += `&fpid=${fpid}`
}

return papiUrl;
}
Expand Down Expand Up @@ -246,7 +273,7 @@ const getBidRequestData = (reqBidsConfigObj, callback, moduleConfig, userConsent
// Get the required config
const { customerId, bidders } = extractConfig(moduleConfig, reqBidsConfigObj);
// Get PAPI URL
const papiUrl = getPapiUrl(customerId, extractConsent(userConsent) || {})
const papiUrl = getPapiUrl(customerId, extractConsent(userConsent) || {}, extractFpid())
// Call PAPI
getTargetingDataFromPapi(papiUrl)
.then((papiResponse) => {
Expand Down
44 changes: 30 additions & 14 deletions test/spec/modules/1plusXRtdProvider_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
extractConsent,
getPapiUrl
} from 'modules/1plusXRtdProvider';
import assert from 'assert';
import { extractFpid } from '../../../modules/1plusXRtdProvider';

describe('1plusXRtdProvider', () => {
// Fake server config
Expand Down Expand Up @@ -276,41 +278,50 @@ describe('1plusXRtdProvider', () => {

it('throws an error if the consent is malformed', () => {
const consent1 = {
gdpr: {
gdprApplies: 1
}
}
const consent2 = {
gdpr: {
consentString: 'myConsent'
}
}
const consent3 = {
const consent2 = {
gdpr: {
gdprApplies: 1,
consentString: 3
}
}
const consent4 = {
const consent3 = {
gdpr: {
gdprApplies: 'yes',
consentString: 'myConsent'
}
}
const consent5 = {
gdprApplies: 1,
consentString: 'myConsent'
const consent4 = {
gdpr: {}
}

for (const consent in [consent1, consent2, consent3, consent4, consent5]) {
for (const consent of [consent1, consent2, consent3, consent4]) {
var failed = false;
try {
extractConsent(consent)
assert(false, 'Should be throwing an exception')
} catch (e) { }
} catch (e) {
failed = true;
} finally {
assert(failed, 'Should be throwing an exception')
}
}
})
})

describe('extractFpid', () => {
it('correctly extracts an ope fpid if present', () => {
window.localStorage.setItem('ope_fpid', 'oneplusx_test_key')
const id1 = extractFpid()
window.localStorage.removeItem('ope_fpid')
const id2 = extractFpid()
expect(id1).to.equal('oneplusx_test_key')
expect(id2).to.equal(null)
})
})

describe('getPapiUrl', () => {
const customer = 'acme'
const consent = {
Expand All @@ -320,11 +331,16 @@ describe('1plusXRtdProvider', () => {
}
}

it('correctly builds URLs based on consent', () => {
it('correctly builds URLs if gdpr parameters are present', () => {
const url1 = getPapiUrl(customer)
const url2 = getPapiUrl(customer, extractConsent(consent))
expect(['&consent_string=myConsent&gdpr_applies=1', '&gdpr_applies=1&consent_string=myConsent']).to.contain(url2.replace(url1, ''))
})

it('correctly builds URLs if fpid parameters are present')
const url1 = getPapiUrl(customer)
const url2 = getPapiUrl(customer, {}, 'my_first_party_id')
expect(url2.replace(url1, '')).to.equal('&fpid=my_first_party_id')
})

describe('updateBidderConfig', () => {
Expand Down