From c4eed89300d2bad79c51fb3e7a53486d4d92444a Mon Sep 17 00:00:00 2001 From: Luca Lavarini Date: Fri, 14 Oct 2022 15:12:01 +0200 Subject: [PATCH] init --- modules/1plusXRtdProvider.js | 37 ++++++++++++++--- test/spec/modules/1plusXRtdProvider_spec.js | 44 ++++++++++++++------- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/modules/1plusXRtdProvider.js b/modules/1plusXRtdProvider.js index 317bb4a47af..390f4f4cd81 100644 --- a/modules/1plusXRtdProvider.js +++ b/modules/1plusXRtdProvider.js @@ -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 @@ -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 @@ -65,8 +72,8 @@ 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, @@ -74,13 +81,30 @@ export const extractConsent = ({ gdpr }) => { } 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}`; @@ -89,6 +113,9 @@ export const getPapiUrl = (customerId, consent) => { papiUrl += `&${key}=${value}` }) } + if (fpid) { + papiUrl += `&fpid=${fpid}` + } return papiUrl; } @@ -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) => { diff --git a/test/spec/modules/1plusXRtdProvider_spec.js b/test/spec/modules/1plusXRtdProvider_spec.js index dae0e313387..496c005f470 100644 --- a/test/spec/modules/1plusXRtdProvider_spec.js +++ b/test/spec/modules/1plusXRtdProvider_spec.js @@ -9,6 +9,8 @@ import { extractConsent, getPapiUrl } from 'modules/1plusXRtdProvider'; +import assert from 'assert'; +import { extractFpid } from '../../../modules/1plusXRtdProvider'; describe('1plusXRtdProvider', () => { // Fake server config @@ -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 = { @@ -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', () => {