Skip to content

Commit

Permalink
Support additional AMP params (#1379)
Browse files Browse the repository at this point in the history
  • Loading branch information
SerhiiNahornyi authored Aug 2, 2021
1 parent 2e20eda commit 6eae644
Show file tree
Hide file tree
Showing 9 changed files with 350 additions and 20 deletions.
20 changes: 13 additions & 7 deletions docs/endpoints/openrtb2/amp.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,14 @@ This endpoint supports the following query parameters:
6. `curl` - the canonical URL of the page
7. `timeout` - the publisher-specified timeout for the RTC callout
- A configuration option `amp.default-timeout-ms` may be set to account for estimated latency so that Prebid Server can handle timeouts from adapters and respond to the AMP RTC request before it times out.
8. `us_privacy` - CCPA value for request.
9. `gdpr_consent` - GDPR value for request.
8. `gdpr_consent` - consent string for request.
9. `consent_string` - consent string for request.
10. `debug` - When set to `1`, the responses will contain extra info for debugging.
11. `account` - accountId parameter for Site object.
12. `slot` - tagId parameter for Imp object.
13. `gdpr_applies` - GDPR param for Regs Ext object.
14. `consent_type` - param to define what type of consent_string passed.
15. `attl_consent` - consentedProviders param for User Ext ConsentedProvidersSettings object.


For information on how these get from AMP into this endpoint, see [this pull request adding the query params to the Prebid callout](https://github.com/ampproject/amphtml/pull/14155) and [this issue adding support for network-level RTC macros](https://github.com/ampproject/amphtml/issues/12374).
Expand All @@ -130,11 +133,14 @@ If present, these will override parts of your Stored Request.
1. `ow`, `oh`, `w`, `h`, and/or `ms` will be used to set `request.imp[0].banner.format` if `request.imp[0].banner` is present.
2. `curl` will be used to set `request.site.page`
3. `timeout` will generally be used to set `request.tmax`. However, the Prebid Server host can [configure](../../config.md) their deploy to reduce this timeout for technical reasons.
4. `us_privacy` will be used to set `request.regs.ext.us_privacy`
5. `debug` will be used to set `request.test`, causing the `response.debug` to have extra debugging info in it.
6. `account` - will be used to set `site.publisher.id` parameter for Site object.
7. `slot` - will be used to set `tagId` parameter to overwrite Imp object.
8. `gdpr_consent` will be used to set `user.ext.consent`.
4. `debug` will be used to set `request.test`, causing the `response.debug` to have extra debugging info in it.
5. `account` - will be used to set `site.publisher.id` parameter for Site object.
6. `slot` - will be used to set `tagId` parameter to overwrite Imp object.
7. `gdpr_applies` will be used to set `request.regs.ext.gdpr`
8. `consent_type` will be used to check what should be done with consent string
9. `attl_consent` will be used to set `user.ext.ConsentedProvidersSettings.consented_providers`.
10. `gdpr_consent` will be used to set `request.regs.ext.us_privacy` or `user.ext.consent`
11. `consent_string` will be used to set `request.regs.ext.us_privacy` or `user.ext.consent`. This param has bigger priority then `gdpr_consent`.



Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.prebid.server.auction.model;

/**
* Describes consent types that can be present in `consent_type` amp query param
*/
public enum ConsentType {

tcfV1, tcfV2, usPrivacy, unknown;
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@
import org.prebid.server.auction.StoredRequestProcessor;
import org.prebid.server.auction.TimeoutResolver;
import org.prebid.server.auction.model.AuctionContext;
import org.prebid.server.auction.model.ConsentType;
import org.prebid.server.exception.InvalidRequestException;
import org.prebid.server.json.JacksonMapper;
import org.prebid.server.metric.MetricName;
import org.prebid.server.model.Endpoint;
import org.prebid.server.model.HttpRequestContext;
import org.prebid.server.privacy.ccpa.Ccpa;
import org.prebid.server.privacy.gdpr.TcfDefinerService;
import org.prebid.server.proto.openrtb.ext.request.ConsentedProvidersSettings;
import org.prebid.server.proto.openrtb.ext.request.ExtMediaTypePriceGranularity;
import org.prebid.server.proto.openrtb.ext.request.ExtPriceGranularity;
import org.prebid.server.proto.openrtb.ext.request.ExtRegs;
Expand Down Expand Up @@ -76,6 +78,9 @@ public class AmpRequestFactory {
private static final String TIMEOUT_REQUEST_PARAM = "timeout";
private static final String GDPR_CONSENT_PARAM = "gdpr_consent";
private static final String CONSENT_PARAM = "consent_string";
private static final String GDPR_APPLIES_PARAM = "gdpr_applies";
private static final String CONSENT_TYPE_PARAM = "consent_type";
private static final String ATTL_CONSENT_PARAM = "attl_consent";

private static final int NO_LIMIT_SPLIT_MODE = -1;
private static final String AMP_CHANNEL = "amp";
Expand Down Expand Up @@ -154,14 +159,17 @@ private Future<BidRequest> parseBidRequest(HttpRequestContext httpRequest, Aucti
return Future.failedFuture(new InvalidRequestException("AMP requests require an AMP tag_id"));
}

final ConsentType consentType = consentTypeFromQueryStringParams(httpRequest);
final String consentString = consentStringFromQueryStringParams(httpRequest);
final String attlConsent = attlConsentFromQueryStringParams(httpRequest);
final Integer gdpr = gdprFromQueryStringParams(httpRequest);
final Integer debug = debugFromQueryStringParam(httpRequest);
final Long timeout = timeoutFromQueryString(httpRequest);

final BidRequest bidRequest = BidRequest.builder()
.site(createSite(httpRequest))
.user(createUser(consentString))
.regs(createRegs(consentString))
.user(createUser(consentType, consentString, attlConsent))
.regs(createRegs(consentString, consentType, gdpr))
.test(debug)
.tmax(timeout)
.ext(createExt(httpRequest, tagId, debug))
Expand All @@ -186,16 +194,33 @@ private static Site createSite(HttpRequestContext httpRequest) {
: null;
}

private static User createUser(String consentString) {
return StringUtils.isNotBlank(consentString) && TcfDefinerService.isConsentStringValid(consentString)
? User.builder().ext(ExtUser.builder().consent(consentString).build()).build()
: null;
private static User createUser(ConsentType consentType, String consentString, String attlConsent) {
final boolean tcfV2ConsentProvided = (StringUtils.isNotBlank(consentString)
&& TcfDefinerService.isConsentStringValid(consentString))
&& (consentType == null || consentType == ConsentType.tcfV2);

if (StringUtils.isNotBlank(attlConsent) || tcfV2ConsentProvided) {
final ExtUser.ExtUserBuilder userExtBuilder = ExtUser.builder();
if (tcfV2ConsentProvided) {
userExtBuilder.consent(consentString);
}
if (StringUtils.isNotBlank(attlConsent)) {
userExtBuilder.consentedProvidersSettings(ConsentedProvidersSettings.of(attlConsent));
}
return User.builder().ext(userExtBuilder.build()).build();
}

return null;
}

private static Regs createRegs(String consentString) {
return StringUtils.isNotBlank(consentString) && Ccpa.isValid(consentString)
? Regs.of(null, ExtRegs.of(null, consentString))
: null;
private static Regs createRegs(String consentString, ConsentType consentType, Integer gdpr) {
final boolean ccpaProvided = Ccpa.isValid(consentString)
&& (consentType == null || consentType == ConsentType.usPrivacy);
if (ccpaProvided || gdpr != null) {
return Regs.of(null, ExtRegs.of(gdpr, ccpaProvided ? consentString : null));
}

return null;
}

private static ExtRequest createExt(HttpRequestContext httpRequest, String tagId, Integer debug) {
Expand All @@ -222,13 +247,45 @@ private static String canonicalUrl(HttpRequestContext httpRequest) {
}
}

private static ConsentType consentTypeFromQueryStringParams(HttpRequestContext httpRequest) {
final String consentTypeParam = httpRequest.getQueryParams().get(CONSENT_TYPE_PARAM);
if (consentTypeParam == null) {
return null;
}
switch (consentTypeParam) {
case "1":
return ConsentType.tcfV1;
case "2":
return ConsentType.tcfV2;
case "3":
return ConsentType.usPrivacy;
default:
return ConsentType.unknown;
}
}

private static String consentStringFromQueryStringParams(HttpRequestContext httpRequest) {
final String requestConsentParam = httpRequest.getQueryParams().get(CONSENT_PARAM);
final String requestGdprConsentParam = httpRequest.getQueryParams().get(GDPR_CONSENT_PARAM);

return ObjectUtils.firstNonNull(requestConsentParam, requestGdprConsentParam);
}

private static String attlConsentFromQueryStringParams(HttpRequestContext httpRequest) {
return httpRequest.getQueryParams().get(ATTL_CONSENT_PARAM);
}

private static Integer gdprFromQueryStringParams(HttpRequestContext httpRequest) {
final String gdprAppliesParam = httpRequest.getQueryParams().get(GDPR_APPLIES_PARAM);
if (StringUtils.equals(gdprAppliesParam, "true")) {
return 1;
} else if (StringUtils.equals(gdprAppliesParam, "false")) {
return 0;
}

return null;
}

private static Long timeoutFromQueryString(HttpRequestContext httpRequest) {
final String timeoutQueryParam = httpRequest.getQueryParams().get(TIMEOUT_REQUEST_PARAM);
if (timeoutQueryParam == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.prebid.server.proto.openrtb.ext.request;

import lombok.AllArgsConstructor;
import lombok.Value;

@AllArgsConstructor(staticName = "of")
@Value
public class ConsentedProvidersSettings {

String consentedProviders;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.prebid.server.proto.openrtb.ext.request;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Builder;
Expand Down Expand Up @@ -46,6 +47,12 @@ public class ExtUser extends FlexibleExtension {
*/
JsonNode digitrust;

/**
* Defines the contract for bidrequest.user.ext.ConsentedProvidersSettings
*/
@JsonProperty("ConsentedProvidersSettings")
ConsentedProvidersSettings consentedProvidersSettings;

@JsonIgnore
public boolean isEmpty() {
return Objects.equals(this, EMPTY);
Expand Down
Loading

0 comments on commit 6eae644

Please sign in to comment.