Skip to content

Commit

Permalink
✨ Consent Metadata macros in ads RTC and amp-analytics (#30152)
Browse files Browse the repository at this point in the history
* analytics & ads macro

* Tests and examples

* Suggested changes

* Moving e2e url to fixture
  • Loading branch information
Micajuine Ho committed Oct 16, 2020
1 parent 17f592e commit 3c0276c
Show file tree
Hide file tree
Showing 14 changed files with 111 additions and 17 deletions.
11 changes: 9 additions & 2 deletions build-system/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -821,11 +821,18 @@ app.post('/get-consent-no-prompt/', (req, res) => {

app.post('/check-consent', (req, res) => {
cors.assertCors(req, res, ['POST']);
res.json({
const response = {
'consentRequired': req.query.consentRequired === 'true',
'consentStateValue': req.query.consentStateValue,
'consentString': req.query.consentString,
'expireCache': req.query.expireCache === 'true',
});
};
if (req.query.consentMetadata) {
response['consentMetadata'] = JSON.parse(
req.query.consentMetadata.replace(/'/g, '"')
);
}
res.json(response);
});

// Proxy with local JS.
Expand Down
4 changes: 2 additions & 2 deletions examples/amp-consent/amp-consent-amp-ad.amp.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ <h2>Doubleclick with RTC (Blocked _till_responded)</h2>
width="320" height="50"
type="doubleclick"
data-slot="/4119129/mobile_ad_banner"
rtc-config='{"vendors": {"fakeVendor": {"SLOT_ID": "1"}}, "urls": ["https://localhost:4443/posts?slot_id=1&consent_state=CONSENT_STATE&consent_string=CONSENT_STRING"], "timeoutMillis": 500}'>
rtc-config='{"vendors": {"fakeVendor": {"SLOT_ID": "1"}}, "urls": ["https://localhost:4443/posts?slot_id=1&consent_state=CONSENT_STATE&consent_string=CONSENT_STRING&gdprApplies=CONSENT_METADATA(gdprApplies)&additionalConsent=CONSENT_METADATA(additionalConsent)&consentStringType=CONSENT_METADATA(consentStringType)"], "timeoutMillis": 500}'>
</amp-ad>

<h2>Doubleclick with RTC (Blocked by _till_accepted)</h2>
<amp-ad data-block-on-consent='_till_accepted'
width="320" height="50"
type="doubleclick"
data-slot="/4119129/mobile_ad_banner"
rtc-config='{"vendors": {"fakeVendor": {"SLOT_ID": "1"}}, "urls": ["https://localhost:4443/posts?slot_id=1&consent_state=CONSENT_STATE&consent_string=CONSENT_STRING"], "timeoutMillis": 500}'>
rtc-config='{"vendors": {"fakeVendor": {"SLOT_ID": "1"}}, "urls": ["https://localhost:4443/posts?slot_id=1&consent_state=CONSENT_STATE&consent_string=CONSENT_STRING&gdprApplies=CONSENT_METADATA(gdprApplies)&additionalConsent=CONSENT_METADATA(additionalConsent)&consentStringType=CONSENT_METADATA(consentStringType)"], "timeoutMillis": 500}'>
</amp-ad>

</amp-ad>
Expand Down
12 changes: 9 additions & 3 deletions extensions/amp-a4a/0.1/amp-a4a.js
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,11 @@ export class AmpA4A extends AMP.BaseElement {

return /** @type {!Promise<?string>} */ (this.getAdUrl(
{consentState, consentString, gdprApplies},
this.tryExecuteRealTimeConfig_(consentState, consentString)
this.tryExecuteRealTimeConfig_(
consentState,
consentString,
/** @type {?Object<string, string|number|boolean|undefined>} */ (consentMetadata)
)
));
})
// This block returns the (possibly empty) response to the XHR request.
Expand Down Expand Up @@ -2203,15 +2207,17 @@ export class AmpA4A extends AMP.BaseElement {
* the rtc-config attribute on the amp-ad element, warn.
* @param {?CONSENT_POLICY_STATE} consentState
* @param {?string} consentString
* @param {?Object<string, string|number|boolean|undefined>} consentMetadata
* @return {Promise<!Array<!rtcResponseDef>>|undefined}
*/
tryExecuteRealTimeConfig_(consentState, consentString) {
tryExecuteRealTimeConfig_(consentState, consentString, consentMetadata) {
if (!!AMP.RealTimeConfigManager) {
try {
return new AMP.RealTimeConfigManager(this).maybeExecuteRealTimeConfig(
this.getCustomRealTimeConfigMacros_(),
consentState,
consentString
consentString,
consentMetadata
);
} catch (err) {
user().error(TAG, 'Could not perform Real Time Config.', err);
Expand Down
20 changes: 19 additions & 1 deletion extensions/amp-a4a/0.1/real-time-config-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ export class RealTimeConfigManager {

/** @private {?string} */
this.consentString_ = null;

/** @private {?Object<string, string|number|boolean|undefined>} */
this.consentMetadata_ = null;
}

/**
Expand Down Expand Up @@ -166,15 +169,22 @@ export class RealTimeConfigManager {
* substitutions available to use.
* @param {?CONSENT_POLICY_STATE} consentState
* @param {?string} consentString
* @param {?Object<string, string|number|boolean|undefined>} consentMetadata
* @return {Promise<!Array<!rtcResponseDef>>|undefined}
* @visibleForTesting
*/
maybeExecuteRealTimeConfig(customMacros, consentState, consentString) {
maybeExecuteRealTimeConfig(
customMacros,
consentState,
consentString,
consentMetadata
) {
if (!this.validateRtcConfig_(this.a4aElement_.element)) {
return;
}
this.consentState_ = consentState;
this.consentString_ = consentString;
this.consentMetadata_ = consentMetadata;
this.modifyRtcConfigForConsentStateSettings();
customMacros = this.assignMacros(customMacros);
this.rtcStartTime_ = Date.now();
Expand Down Expand Up @@ -276,6 +286,14 @@ export class RealTimeConfigManager {
macros['TIMEOUT'] = () => this.rtcConfig_.timeoutMillis;
macros['CONSENT_STATE'] = () => this.consentState_;
macros['CONSENT_STRING'] = () => this.consentString_;
macros[
'CONSENT_METADATA'
] = /** @type {!../../../src/service/variable-source.AsyncResolverDef} */ ((
key
) => {
userAssert(key, 'CONSENT_METADATA macro must contian a key');
return this.consentMetadata_ ? this.consentMetadata_[key] : null;
});
return macros;
}

Expand Down
13 changes: 10 additions & 3 deletions extensions/amp-a4a/0.1/test/test-amp-a4a.js
Original file line number Diff line number Diff line change
Expand Up @@ -2447,7 +2447,11 @@ describe('amp-a4a', () => {
a4a = new MockA4AImpl(a4aElement);
consentString = 'test-consent-string';
gdprApplies = true;
consentMetadata = {gdprApplies};
consentMetadata = {
gdprApplies,
'consentStringType': 1,
'additionalConsent': 'abc123',
};
return fixture;
});

Expand Down Expand Up @@ -2492,7 +2496,8 @@ describe('amp-a4a', () => {
expect(
tryExecuteRealTimeConfigSpy.withArgs(
CONSENT_POLICY_STATE.SUFFICIENT,
consentString
consentString,
consentMetadata
)
).calledOnce;
});
Expand Down Expand Up @@ -2545,7 +2550,8 @@ describe('amp-a4a', () => {
expect(
tryExecuteRealTimeConfigSpy.withArgs(
CONSENT_POLICY_STATE.SUFFICIENT,
consentString
consentString,
consentMetadata
)
).calledOnce;
});
Expand Down Expand Up @@ -2590,6 +2596,7 @@ describe('amp-a4a', () => {
expect(
tryExecuteRealTimeConfigSpy.withArgs(
CONSENT_POLICY_STATE.UNKNOWN,
null,
null
)
).calledOnce;
Expand Down
15 changes: 15 additions & 0 deletions extensions/amp-analytics/0.1/test/test-requests.js
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,21 @@ describes.realWin('Requests', {amp: 1}, (env) => {
expect(spy).to.be.calledWith('r1&test&test2');
});

it('should replace dynamic bindings CONSENT_METADATA', async () => {
const spy = env.sandbox.spy();
const r = {
'baseUrl':
'r1&$CONSENT_METADATA(gdprApplies)test&${consentMetadata(additionalConsent)}test2',
};
const handler = createRequestHandler(r, spy);
const expansionOptions = new ExpansionOptions({
'consentMetadata': 'CONSENT_METADATA',
});
handler.send({}, {}, expansionOptions);
await macroTask();
expect(spy).to.be.calledWith('r1&test&test2');
});

it('COOKIE read cookie value', function* () {
const spy = env.sandbox.spy();
const r = {'baseUrl': 'r1&c1=COOKIE(test)&c2=${cookie(test)}'};
Expand Down
19 changes: 19 additions & 0 deletions extensions/amp-analytics/0.1/test/test-variables.js
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,25 @@ describes.fakeWin('amp-analytics.VariableService', {amp: true}, (env) => {
);
});

it('replaces CONSENT_METADATA', () => {
window.sandbox.stub(Services, 'consentPolicyServiceForDocOrNull').returns(
Promise.resolve({
getConsentMetadataInfo: () => {
return {
'gdprApplies': true,
'additionalConsent': 'abc123',
'consentStringType': 1,
};
},
})
);

return check(
'CONSENT_METADATA(gdprApplies)&CONSENT_METADATA(additionalConsent)&CONSENT_METADATA(consentStringType)&CONSENT_METADATA(invalid_key)',
'true&abc123&1&'
);
});

it('"COOKIE" resolves cookie value', async () => {
doc.cookie = 'test=123';
await check('COOKIE(test)', '123');
Expand Down
1 change: 1 addition & 0 deletions extensions/amp-analytics/0.1/test/test-vendors.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describes.realWin(
elementMacros = {
'COOKIE': null,
'CONSENT_STATE': null,
'CONSENT_METADATA': null,
};
});

Expand Down
23 changes: 22 additions & 1 deletion extensions/amp-analytics/0.1/variables.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
getActiveExperimentBranches,
getExperimentBranch,
} from '../../../src/experiments';
import {getConsentPolicyState} from '../../../src/consent';
import {getConsentMetadata, getConsentPolicyState} from '../../../src/consent';
import {
getServiceForDoc,
getServicePromiseForDoc,
Expand Down Expand Up @@ -278,6 +278,11 @@ export class VariableService {
'COOKIE': (name) =>
cookieReader(this.ampdoc_.win, dev().assertElement(element), name),
'CONSENT_STATE': getConsentStateStr(element),
'CONSENT_METADATA': (key) =>
getConsentMetadataValue(
element,
userAssert(key, 'CONSENT_METADATA macro must contain a key')
),
};
const perfMacros = isInFie(element)
? {}
Expand Down Expand Up @@ -530,6 +535,22 @@ function getConsentStateStr(element) {
});
}

/**
* Get the associated value from the resolved consent metadata object
* @param {!Element} element
* @param {string} key
* @return {!Promise<?Object>}
*/
function getConsentMetadataValue(element, key) {
// Get the metadata using the default policy id
return getConsentMetadata(element).then((consentMetadata) => {
if (!consentMetadata) {
return null;
}
return consentMetadata[key];
});
}

/**
* Converts string to boolean
* @param {string} str
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describes.endtoend(
'amp-consent',
{
testUrl:
'http://localhost:8000/test/manual/amp-consent/amp-consent-basic-uses.amp.html#amp-geo=de',
'http://localhost:8000/test/fixtures/e2e/amp-consent/amp-consent-basic-uses.amp.html#amp-geo=de',
// TODO (micajuineho): Add shadow-demo after #25985 is fixed and viewer-demo when...
environments: ['single'],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describes.endtoend(
'amp-consent',
{
testUrl:
'http://localhost:8000/test/manual/amp-consent/amp-consent-basic-uses.amp.html#amp-geo=mx',
'http://localhost:8000/test/fixtures/e2e/amp-consent/amp-consent-basic-uses.amp.html#amp-geo=mx',
// TODO (micajuineho): Add shadow-demo after #25985 is fixed, and viewer-demo when...
environments: ['single'],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describes.endtoend(
'amp-consent',
{
testUrl:
'http://localhost:8000/test/manual/amp-consent/amp-consent-basic-uses.amp.html#amp-geo=us',
'http://localhost:8000/test/fixtures/e2e/amp-consent/amp-consent-basic-uses.amp.html#amp-geo=us',
// TODO (micajuineho): Add shadow-demo after #25985 is fixed and viewer-demo when...
environments: ['single'],
},
Expand Down
4 changes: 2 additions & 2 deletions src/consent.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,10 @@ export function getConsentPolicyInfo(element, policyId) {

/**
* @param {!Element|!ShadowRoot} element
* @param {string} policyId
* @param {string=} policyId
* @return {!Promise<?Object|undefined>}
*/
export function getConsentMetadata(element, policyId) {
export function getConsentMetadata(element, policyId = 'default') {
// Return the stored consent metadata.
return Services.consentPolicyServiceForDocOrNull(element).then(
(consentPolicy) => {
Expand Down

0 comments on commit 3c0276c

Please sign in to comment.