Skip to content

Commit

Permalink
AMP additionalConsent (prebid#2378)
Browse files Browse the repository at this point in the history
  • Loading branch information
guscarreon authored Oct 24, 2022
1 parent d162aa9 commit 718628c
Show file tree
Hide file tree
Showing 9 changed files with 885 additions and 108 deletions.
38 changes: 20 additions & 18 deletions amp/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,19 @@ import (

// Params defines the parameters of an AMP request.
type Params struct {
Account string
CanonicalURL string
Consent string
ConsentType int64
Debug bool
GdprApplies *bool
Origin string
Size Size
Slot string
StoredRequestID string
Targeting string
Timeout *uint64
Account string
AdditionalConsent string
CanonicalURL string
Consent string
ConsentType int64
Debug bool
GdprApplies *bool
Origin string
Size Size
Slot string
StoredRequestID string
Targeting string
Timeout *uint64
}

// Size defines size information of an AMP request.
Expand Down Expand Up @@ -154,12 +155,13 @@ func ParseParams(httpRequest *http.Request) (Params, error) {
}

params := Params{
Account: query.Get("account"),
CanonicalURL: query.Get("curl"),
Consent: chooseConsent(query.Get("consent_string"), query.Get("gdpr_consent")),
ConsentType: parseInt(query.Get("consent_type")),
Debug: query.Get("debug") == "1",
Origin: query.Get("__amp_source_origin"),
Account: query.Get("account"),
AdditionalConsent: query.Get("addtl_consent"),
CanonicalURL: query.Get("curl"),
Consent: chooseConsent(query.Get("consent_string"), query.Get("gdpr_consent")),
ConsentType: parseInt(query.Get("consent_type")),
Debug: query.Get("debug") == "1",
Origin: query.Get("__amp_source_origin"),
Size: Size{
Height: parseInt(query.Get("h")),
Multisize: parseMultisize(query.Get("ms")),
Expand Down
44 changes: 44 additions & 0 deletions endpoints/openrtb2/amp_auction.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,10 @@ func (deps *endpointDeps) overrideWithParams(ampParams amp.Params, req *openrtb2
req.Imp[0].TagID = ampParams.Slot
}

if err := setConsentedProviders(req, ampParams); err != nil {
return []error{err}
}

policyWriter, policyWriterErr := amp.ReadPolicy(ampParams, deps.cfg.GDPR.Enabled)
if policyWriterErr != nil {
return []error{policyWriterErr}
Expand All @@ -480,6 +484,46 @@ func (deps *endpointDeps) overrideWithParams(ampParams amp.Params, req *openrtb2
return nil
}

// setConsentedProviders sets the addtl_consent value to user.ext.ConsentedProvidersSettings.consented_providers
// in its orginal Google Additional Consent string format and user.ext.consented_providers_settings.consented_providers
// that is an array of ints that contains the elements found in addtl_consent
func setConsentedProviders(req *openrtb2.BidRequest, ampParams amp.Params) error {
if len(ampParams.AdditionalConsent) > 0 {
reqWrap := &openrtb_ext.RequestWrapper{BidRequest: req}

userExt, err := reqWrap.GetUserExt()
if err != nil {
return err
}

// Parse addtl_consent, that is supposed to come formatted as a Google Additional Consent string, into array of ints
consentedProvidersList := openrtb_ext.ParseConsentedProvidersString(ampParams.AdditionalConsent)

// Set user.ext.consented_providers_settings.consented_providers if elements where found
if len(consentedProvidersList) > 0 {
cps := userExt.GetConsentedProvidersSettingsOut()
if cps == nil {
cps = &openrtb_ext.ConsentedProvidersSettingsOut{}
}
cps.ConsentedProvidersList = append(cps.ConsentedProvidersList, consentedProvidersList...)
userExt.SetConsentedProvidersSettingsOut(cps)
}

// Copy addtl_consent into user.ext.ConsentedProvidersSettings.consented_providers as is
cps := userExt.GetConsentedProvidersSettingsIn()
if cps == nil {
cps = &openrtb_ext.ConsentedProvidersSettingsIn{}
}
cps.ConsentedProvidersString = ampParams.AdditionalConsent
userExt.SetConsentedProvidersSettingsIn(cps)

if err := reqWrap.RebuildRequest(); err != nil {
return err
}
}
return nil
}

// setTargeting merges "targeting" to imp[0].ext.data
func setTargeting(req *openrtb2.BidRequest, targeting string) error {
if len(targeting) == 0 {
Expand Down
233 changes: 233 additions & 0 deletions endpoints/openrtb2/amp_auction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/julienschmidt/httprouter"
"github.com/prebid/openrtb/v17/openrtb2"
"github.com/prebid/prebid-server/amp"
"github.com/prebid/prebid-server/analytics"
analyticsConf "github.com/prebid/prebid-server/analytics/config"
"github.com/prebid/prebid-server/config"
Expand Down Expand Up @@ -47,6 +48,7 @@ func TestGoodAmpRequests(t *testing.T) {
desc: "Valid, consent handling in query",
dir: "sample-requests/amp/consent-through-query/",
testFiles: []string{
"addtl-consent-through-query.json",
"gdpr-tcf1-consent-through-query.json",
"gdpr-tcf2-consent-through-query.json",
"gdpr-legacy-tcf2-consent-through-query.json",
Expand Down Expand Up @@ -376,6 +378,237 @@ func TestGDPRConsent(t *testing.T) {
}
}

func TestOverrideWithParams(t *testing.T) {
e := &endpointDeps{
cfg: &config.Configuration{
GDPR: config.GDPR{
Enabled: true,
},
},
}

type testInput struct {
ampParams amp.Params
bidRequest *openrtb2.BidRequest
}
type testOutput struct {
bidRequest *openrtb2.BidRequest
errorMsgs []string
}
testCases := []struct {
desc string
given testInput
expected testOutput
}{
{
desc: "bid request with no Site field - amp.Params empty - expect Site to be added",
given: testInput{
ampParams: amp.Params{},
bidRequest: &openrtb2.BidRequest{
Imp: []openrtb2.Imp{{Banner: &openrtb2.Banner{Format: []openrtb2.Format{}}}},
},
},
expected: testOutput{
bidRequest: &openrtb2.BidRequest{
Imp: []openrtb2.Imp{{Banner: &openrtb2.Banner{Format: []openrtb2.Format{}}}},
Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)},
},
errorMsgs: nil,
},
},
{
desc: "amp.Params with Size field - expect Site and Banner format fields to be added",
given: testInput{
ampParams: amp.Params{
Size: amp.Size{
Width: 480,
Height: 320,
},
},
bidRequest: &openrtb2.BidRequest{
Imp: []openrtb2.Imp{{Banner: &openrtb2.Banner{Format: []openrtb2.Format{}}}},
},
},
expected: testOutput{
bidRequest: &openrtb2.BidRequest{
Imp: []openrtb2.Imp{
{
Banner: &openrtb2.Banner{
Format: []openrtb2.Format{
{
W: 480,
H: 320,
},
},
},
},
},
Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)},
},
errorMsgs: nil,
},
},
{
desc: "amp.Params with CanonicalURL field - expect Site to be aded with Page and Domain fields",
given: testInput{
ampParams: amp.Params{CanonicalURL: "http://www.foobar.com"},
bidRequest: &openrtb2.BidRequest{Imp: []openrtb2.Imp{{Banner: &openrtb2.Banner{Format: []openrtb2.Format{}}}}},
},
expected: testOutput{
bidRequest: &openrtb2.BidRequest{
Imp: []openrtb2.Imp{{Banner: &openrtb2.Banner{Format: []openrtb2.Format{}}}},
Site: &openrtb2.Site{
Page: "http://www.foobar.com",
Domain: "www.foobar.com",
Ext: json.RawMessage(`{"amp":1}`),
},
},
errorMsgs: nil,
},
},
{
desc: "bid request with malformed User.Ext - amp.Params with AdditionalConsent - expect error",
given: testInput{
ampParams: amp.Params{AdditionalConsent: "1~X.X.X.X"},
bidRequest: &openrtb2.BidRequest{
Imp: []openrtb2.Imp{{Banner: &openrtb2.Banner{Format: []openrtb2.Format{}}}},
User: &openrtb2.User{Ext: json.RawMessage(`malformed`)},
},
},
expected: testOutput{
bidRequest: &openrtb2.BidRequest{
Imp: []openrtb2.Imp{{Banner: &openrtb2.Banner{Format: []openrtb2.Format{}}}},
Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)},
User: &openrtb2.User{Ext: json.RawMessage(`malformed`)},
},
errorMsgs: []string{"invalid character 'm' looking for beginning of value"},
},
},
{
desc: "bid request with valid imp[0].ext - amp.Params with malformed targeting value - expect error because imp[0].ext won't be unable to get merged with targeting values",
given: testInput{
ampParams: amp.Params{Targeting: "{123,}"},
bidRequest: &openrtb2.BidRequest{
Imp: []openrtb2.Imp{
{
Banner: &openrtb2.Banner{Format: []openrtb2.Format{}},
Ext: []byte(`{"appnexus":{"placementId":123}}`),
},
},
},
},
expected: testOutput{
bidRequest: &openrtb2.BidRequest{
Imp: []openrtb2.Imp{
{
Banner: &openrtb2.Banner{Format: []openrtb2.Format{}},
Ext: json.RawMessage(`{"appnexus":{"placementId":123}}`),
},
},
Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)},
},
errorMsgs: []string{"unable to merge imp.ext with targeting data, check targeting data is correct: Invalid JSON Patch"},
},
},
{
desc: "bid request with malformed user.ext.prebid - amp.Params with GDPR consent values - expect policy writer to return error",
given: testInput{
ampParams: amp.Params{
ConsentType: amp.ConsentTCF2,
Consent: "CPdECS0PdECS0ACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA",
},
bidRequest: &openrtb2.BidRequest{
Imp: []openrtb2.Imp{{Banner: &openrtb2.Banner{Format: []openrtb2.Format{}}}},
User: &openrtb2.User{Ext: json.RawMessage(`{"prebid":{malformed}}`)},
},
},
expected: testOutput{
bidRequest: &openrtb2.BidRequest{
Imp: []openrtb2.Imp{{Banner: &openrtb2.Banner{Format: []openrtb2.Format{}}}},
User: &openrtb2.User{Ext: json.RawMessage(`{"prebid":{malformed}}`)},
Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)},
},
errorMsgs: []string{"invalid character 'm' looking for beginning of object key string"},
},
},
}

for _, test := range testCases {
errs := e.overrideWithParams(test.given.ampParams, test.given.bidRequest)

assert.Equal(t, test.expected.bidRequest, test.given.bidRequest, test.desc)
assert.Len(t, errs, len(test.expected.errorMsgs), test.desc)
if len(test.expected.errorMsgs) > 0 {
assert.Equal(t, test.expected.errorMsgs[0], errs[0].Error(), test.desc)
}
}
}

func TestSetConsentedProviders(t *testing.T) {

sampleBidRequest := &openrtb2.BidRequest{}

testCases := []struct {
description string
givenAdditionalConsent string
givenBidRequest *openrtb2.BidRequest
expectedBidRequest *openrtb2.BidRequest
expectedError bool
}{
{
description: "empty additional consent bid request unmodified",
givenAdditionalConsent: "",
givenBidRequest: sampleBidRequest,
expectedBidRequest: sampleBidRequest,
expectedError: false,
},
{
description: "nil bid request, expect error",
givenAdditionalConsent: "ADDITIONAL_CONSENT_STRING",
givenBidRequest: nil,
expectedBidRequest: nil,
expectedError: true,
},
{
description: "malformed user.ext, expect error",
givenAdditionalConsent: "ADDITIONAL_CONSENT_STRING",
givenBidRequest: &openrtb2.BidRequest{
User: &openrtb2.User{
Ext: json.RawMessage(`malformed`),
},
},
expectedBidRequest: &openrtb2.BidRequest{
User: &openrtb2.User{
Ext: json.RawMessage(`malformed`),
},
},
expectedError: true,
},
{
description: "non-empty additional consent bid request will carry this value in user.ext.ConsentedProvidersSettings.consented_providers",
givenAdditionalConsent: "ADDITIONAL_CONSENT_STRING",
givenBidRequest: sampleBidRequest,
expectedBidRequest: &openrtb2.BidRequest{
User: &openrtb2.User{
Ext: json.RawMessage(`{"ConsentedProvidersSettings":{"consented_providers":"ADDITIONAL_CONSENT_STRING"}}`),
},
},
expectedError: false,
},
}

for _, test := range testCases {
err := setConsentedProviders(test.givenBidRequest, amp.Params{AdditionalConsent: test.givenAdditionalConsent})

if test.expectedError {
assert.Error(t, err, test.description)
} else {
assert.NoError(t, err, test.description)
}
assert.Equal(t, test.expectedBidRequest, test.givenBidRequest, test.description)
}
}

func TestCCPAConsent(t *testing.T) {
consent := "1NYN"
existingConsent := "1NNN"
Expand Down
Loading

0 comments on commit 718628c

Please sign in to comment.