Skip to content

Commit 8d75ccf

Browse files
slimkrazyslimkrazy
andauthored
Nodals RTD Module: Add support for publisher to override standard TCF consent checks (prebid#14004)
Co-authored-by: slimkrazy <[email protected]>
1 parent fccbd80 commit 8d75ccf

File tree

2 files changed

+111
-7
lines changed

2 files changed

+111
-7
lines changed

modules/nodalsAiRtdProvider.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class NodalsAiRtdProvider {
5555
const params = config?.params || {};
5656
if (
5757
this.#isValidConfig(params) &&
58-
this.#hasRequiredUserConsent(userConsent)
58+
this.#hasRequiredUserConsent(userConsent, config)
5959
) {
6060
this.#propertyId = params.propertyId;
6161
this.#userConsent = userConsent;
@@ -82,7 +82,7 @@ class NodalsAiRtdProvider {
8282
*/
8383
getTargetingData(adUnitArray, config, userConsent) {
8484
let targetingData = {};
85-
if (!this.#hasRequiredUserConsent(userConsent)) {
85+
if (!this.#hasRequiredUserConsent(userConsent, config)) {
8686
return targetingData;
8787
}
8888
this.#userConsent = userConsent;
@@ -104,7 +104,7 @@ class NodalsAiRtdProvider {
104104
}
105105

106106
getBidRequestData(reqBidsConfigObj, callback, config, userConsent) {
107-
if (!this.#hasRequiredUserConsent(userConsent)) {
107+
if (!this.#hasRequiredUserConsent(userConsent, config)) {
108108
callback();
109109
return;
110110
}
@@ -133,7 +133,7 @@ class NodalsAiRtdProvider {
133133
}
134134

135135
onBidResponseEvent(bidResponse, config, userConsent) {
136-
if (!this.#hasRequiredUserConsent(userConsent)) {
136+
if (!this.#hasRequiredUserConsent(userConsent, config)) {
137137
return;
138138
}
139139
this.#userConsent = userConsent;
@@ -154,7 +154,7 @@ class NodalsAiRtdProvider {
154154
}
155155

156156
onAuctionEndEvent(auctionDetails, config, userConsent) {
157-
if (!this.#hasRequiredUserConsent(userConsent)) {
157+
if (!this.#hasRequiredUserConsent(userConsent, config)) {
158158
return;
159159
}
160160
this.#userConsent = userConsent;
@@ -240,11 +240,12 @@ class NodalsAiRtdProvider {
240240
/**
241241
* Checks if the user has provided the required consent.
242242
* @param {Object} userConsent - User consent object.
243+
* @param {Object} config - Configuration object for the module.
243244
* @returns {boolean} - True if the user consent is valid, false otherwise.
244245
*/
245246

246-
#hasRequiredUserConsent(userConsent) {
247-
if (!userConsent.gdpr || userConsent.gdpr?.gdprApplies === false) {
247+
#hasRequiredUserConsent(userConsent, config) {
248+
if (config?.params?.publisherProvidedConsent === true || !userConsent.gdpr || userConsent.gdpr?.gdprApplies === false) {
248249
return true;
249250
}
250251
if (

test/spec/modules/nodalsAiRtdProvider_spec.js

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ describe('NodalsAI RTD Provider', () => {
147147
const noPurpose1UserConsent = generateGdprConsent({ purpose1Consent: false });
148148
const noPurpose7UserConsent = generateGdprConsent({ purpose7Consent: false });
149149
const outsideGdprUserConsent = generateGdprConsent({ gdprApplies: false });
150+
const leastPermissiveUserConsent = generateGdprConsent({ purpose1Consent: false, purpose7Consent: false, nodalsConsent: false });
150151

151152
beforeEach(() => {
152153
sandbox = sinon.createSandbox();
@@ -263,6 +264,15 @@ describe('NodalsAI RTD Provider', () => {
263264
expect(result).to.be.true;
264265
expect(server.requests.length).to.equal(1);
265266
});
267+
268+
it('should return true with publisherProvidedConsent flag set and least permissive consent', function () {
269+
const configWithManagedConsent = { params: { propertyId: '10312dd2', publisherProvidedConsent: true } };
270+
const result = nodalsAiRtdSubmodule.init(configWithManagedConsent, leastPermissiveUserConsent);
271+
server.respond();
272+
273+
expect(result).to.be.true;
274+
expect(server.requests.length).to.equal(1);
275+
});
266276
});
267277

268278
describe('when initialised with valid config and data already in storage', () => {
@@ -617,6 +627,24 @@ describe('NodalsAI RTD Provider', () => {
617627

618628
expect(result).to.deep.equal({});
619629
});
630+
631+
it('should return targeting data with publisherProvidedConsent flag set and least permissive consent', () => {
632+
createTargetingEngineStub(engineGetTargetingDataReturnValue);
633+
setDataInLocalStorage({
634+
data: successPubEndpointResponse,
635+
createdAt: Date.now(),
636+
});
637+
const configWithManagedConsent = { params: { propertyId: '10312dd2', publisherProvidedConsent: true } };
638+
639+
const result = nodalsAiRtdSubmodule.getTargetingData(
640+
['adUnit1'],
641+
configWithManagedConsent,
642+
leastPermissiveUserConsent
643+
);
644+
server.respond();
645+
646+
expect(result).to.deep.equal(engineGetTargetingDataReturnValue);
647+
});
620648
});
621649

622650
describe('getBidRequestData()', () => {
@@ -703,6 +731,33 @@ describe('NodalsAI RTD Provider', () => {
703731
expect(args[3].campaigns).to.deep.equal(successPubEndpointResponse.campaigns);
704732
expect(server.requests.length).to.equal(0);
705733
});
734+
735+
it('should proxy the correct data to engine.getBidRequestData with publisherProvidedConsent flag set and least permissive consent', () => {
736+
setDataInLocalStorage({
737+
data: successPubEndpointResponse,
738+
createdAt: Date.now(),
739+
});
740+
const engine = createTargetingEngineStub();
741+
const callback = sinon.spy();
742+
const reqBidsConfigObj = {dummy: 'obj'}
743+
const configWithManagedConsent = { params: { propertyId: '10312dd2', publisherProvidedConsent: true } };
744+
nodalsAiRtdSubmodule.getBidRequestData(
745+
reqBidsConfigObj, callback, configWithManagedConsent, leastPermissiveUserConsent
746+
);
747+
server.respond();
748+
749+
expect(callback.called).to.be.false;
750+
expect(engine.init.called).to.be.true;
751+
expect(engine.getBidRequestData.called).to.be.true;
752+
const args = engine.getBidRequestData.getCall(0).args;
753+
expect(args[0]).to.deep.equal(reqBidsConfigObj);
754+
expect(args[1]).to.deep.equal(callback);
755+
expect(args[2]).to.deep.equal(leastPermissiveUserConsent);
756+
expect(args[3].deps).to.deep.equal(successPubEndpointResponse.deps);
757+
expect(args[3].facts).to.deep.include(successPubEndpointResponse.facts);
758+
expect(args[3].campaigns).to.deep.equal(successPubEndpointResponse.campaigns);
759+
expect(server.requests.length).to.equal(0);
760+
});
706761
});
707762

708763
describe('onBidResponseEvent()', () => {
@@ -782,6 +837,30 @@ describe('NodalsAI RTD Provider', () => {
782837
expect(args[2].campaigns).to.deep.equal(successPubEndpointResponse.campaigns);
783838
expect(server.requests.length).to.equal(0);
784839
});
840+
841+
it('should proxy the correct data to engine.onBidResponseEvent with publisherProvidedConsent flag set and least permissive consent', () => {
842+
setDataInLocalStorage({
843+
data: successPubEndpointResponse,
844+
createdAt: Date.now(),
845+
});
846+
const engine = createTargetingEngineStub();
847+
const bidResponse = {dummy: 'obj', 'bid': 'foo'};
848+
const configWithManagedConsent = { params: { propertyId: '10312dd2', publisherProvidedConsent: true } };
849+
nodalsAiRtdSubmodule.onBidResponseEvent(
850+
bidResponse, configWithManagedConsent, leastPermissiveUserConsent
851+
);
852+
server.respond();
853+
854+
expect(engine.init.called).to.be.true;
855+
expect(engine.onBidResponseEvent.called).to.be.true;
856+
const args = engine.onBidResponseEvent.getCall(0).args;
857+
expect(args[0]).to.deep.equal(bidResponse);
858+
expect(args[1]).to.deep.equal(leastPermissiveUserConsent);
859+
expect(args[2].deps).to.deep.equal(successPubEndpointResponse.deps);
860+
expect(args[2].facts).to.deep.include(successPubEndpointResponse.facts);
861+
expect(args[2].campaigns).to.deep.equal(successPubEndpointResponse.campaigns);
862+
expect(server.requests.length).to.equal(0);
863+
});
785864
});
786865

787866
describe('onAuctionEndEvent()', () => {
@@ -861,5 +940,29 @@ describe('NodalsAI RTD Provider', () => {
861940
expect(args[2].campaigns).to.deep.equal(successPubEndpointResponse.campaigns);
862941
expect(server.requests.length).to.equal(0);
863942
});
943+
944+
it('should proxy the correct data to engine.onAuctionEndEvent with publisherProvidedConsent flag set and least permissive consent', () => {
945+
setDataInLocalStorage({
946+
data: successPubEndpointResponse,
947+
createdAt: Date.now(),
948+
});
949+
const engine = createTargetingEngineStub();
950+
const auctionDetails = {dummy: 'obj', auction: 'foo'};
951+
const configWithManagedConsent = { params: { propertyId: '10312dd2', publisherProvidedConsent: true } };
952+
nodalsAiRtdSubmodule.onAuctionEndEvent(
953+
auctionDetails, configWithManagedConsent, leastPermissiveUserConsent
954+
);
955+
server.respond();
956+
957+
expect(engine.init.called).to.be.true;
958+
expect(engine.onAuctionEndEvent.called).to.be.true;
959+
const args = engine.onAuctionEndEvent.getCall(0).args;
960+
expect(args[0]).to.deep.equal(auctionDetails);
961+
expect(args[1]).to.deep.equal(leastPermissiveUserConsent);
962+
expect(args[2].deps).to.deep.equal(successPubEndpointResponse.deps);
963+
expect(args[2].facts).to.deep.include(successPubEndpointResponse.facts);
964+
expect(args[2].campaigns).to.deep.equal(successPubEndpointResponse.campaigns);
965+
expect(server.requests.length).to.equal(0);
966+
});
864967
});
865968
});

0 commit comments

Comments
 (0)