Skip to content

Commit

Permalink
[adform adapter] Pass gdpr & digitrust to bid request (#581)
Browse files Browse the repository at this point in the history
* Added adform info file

* Added Adform adapter and bidder

* Updates from master

* Removed usersyncInfo from Adform adapter. Inverted Imp type check.

* Removed excessive loop

* Updated with the last master

* [adform adapter] Passing gdpr and digitrust to adform request

* Change the buildAdformUrl for increasing readability

* [adform adapter] Early check of the url in adapter constructor
  • Loading branch information
volhalink authored and dbemiller committed Jun 25, 2018
1 parent 26b4093 commit c6578cd
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 28 deletions.
144 changes: 129 additions & 15 deletions adapters/adform/adform.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ import (
"github.com/prebid/prebid-server/openrtb_ext"
"github.com/prebid/prebid-server/pbs"
"golang.org/x/net/context/ctxhttp"
"net/url"
"strconv"
)

type AdformAdapter struct {
http *adapters.HTTPAdapter
URI string
URL *url.URL
version string
}

Expand All @@ -33,6 +35,20 @@ type adformRequest struct {
referer string
userId string
adUnits []*adformAdUnit
gdprApplies string
consent string
digitrust *adformDigitrust
}

type adformDigitrust struct {
Id string `json:"id"`
Version int `json:"version"`
Keyv int `json:"keyv"`
Privacy adformDigitrustPrivacy `json:"privacy"`
}

type adformDigitrustPrivacy struct {
Optout bool `json:"optout"`
}

type adformAdUnit struct {
Expand Down Expand Up @@ -168,6 +184,30 @@ func pbsRequestToAdformRequest(a *AdformAdapter, request *pbs.PBSRequest, bidder

userId, _, _ := request.Cookie.GetUID(a.Name())

gdprApplies := request.ParseGDPR()
if gdprApplies != "0" && gdprApplies != "1" {
gdprApplies = ""
}
consent := request.ParseConsent()
var digitrustData *openrtb_ext.ExtUserDigiTrust
if request.User != nil {
var extUser *openrtb_ext.ExtUser
if err := json.Unmarshal(request.User.Ext, &extUser); err == nil {
digitrustData = extUser.DigiTrust
}
}

var digitrust *adformDigitrust = nil
if digitrustData != nil {
digitrust = new(adformDigitrust)
digitrust.Id = digitrustData.ID
digitrust.Keyv = digitrustData.KeyV
digitrust.Version = 1
digitrust.Privacy = adformDigitrustPrivacy{
Optout: digitrustData.Pref != 0,
}
}

return &adformRequest{
adUnits: adUnits,
ip: request.Device.IP,
Expand All @@ -178,6 +218,9 @@ func pbsRequestToAdformRequest(a *AdformAdapter, request *pbs.PBSRequest, bidder
referer: request.Url,
userId: userId,
tid: request.Tid,
gdprApplies: gdprApplies,
consent: consent,
digitrust: digitrust,
}, nil
}

Expand Down Expand Up @@ -210,21 +253,40 @@ func toPBSBidSlice(adformBids []*adformBid, r *adformRequest) pbs.PBSBidSlice {
// COMMON

func (r *adformRequest) buildAdformUrl(a *AdformAdapter) string {
adUnitsParams := make([]string, 0, len(r.adUnits))
for _, adUnit := range r.adUnits {
str := fmt.Sprintf("mid=%s", adUnit.MasterTagId)
adUnitsParams = append(adUnitsParams, base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString([]byte(str)))
parameters := url.Values{}

if r.advertisingId != "" {
parameters.Add("adid", r.advertisingId)
}
parameters.Add("CC", "1")
parameters.Add("rp", "4")
parameters.Add("fd", "1")
parameters.Add("stid", r.tid)
parameters.Add("ip", r.ip)

priceType := getValidPriceTypeParameter(r.adUnits)
if priceType != "" {
parameters.Add("pt", priceType)
}
uri := a.URI

parameters.Add("gdpr", r.gdprApplies)
parameters.Add("gdpr_consent", r.consent)

URL := *a.URL
URL.RawQuery = parameters.Encode()

uri := URL.String()
if r.isSecure {
uri = strings.Replace(uri, "http://", "https://", 1)
}
adid := ""
if r.advertisingId != "" {
adid = fmt.Sprintf("&adid=%s", r.advertisingId)

adUnitsParams := make([]string, 0, len(r.adUnits))
for _, adUnit := range r.adUnits {
str := fmt.Sprintf("mid=%s", adUnit.MasterTagId)
adUnitsParams = append(adUnitsParams, base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString([]byte(str)))
}
pt := getValidPriceTypeParameter(r.adUnits)
return fmt.Sprintf("%s/?CC=1&rp=4&fd=1&stid=%s&ip=%s%s%s&%s", uri, r.tid, r.ip, adid, pt, strings.Join(adUnitsParams, "&"))

return fmt.Sprintf("%s&%s", uri, strings.Join(adUnitsParams, "&"))
}

func getValidPriceTypeParameter(adUnits []*adformAdUnit) string {
Expand All @@ -243,7 +305,7 @@ func getValidPriceTypeParameter(adUnits []*adformAdUnit) string {
}

if valid {
priceTypeParameter = fmt.Sprintf("&pt=%s", priceType)
priceTypeParameter = priceType
}
return priceTypeParameter
}
Expand All @@ -259,9 +321,19 @@ func (r *adformRequest) buildAdformHeaders(a *AdformAdapter) http.Header {
if r.referer != "" {
header.Set("Referer", r.referer)
}

cookie := make([]string, 0, 2)
if r.userId != "" {
header.Set("Cookie", fmt.Sprintf("uid=%s", r.userId))
cookie = append(cookie, fmt.Sprintf("uid=%s", r.userId))
}
if r.digitrust != nil {
if digitrustBytes, err := json.Marshal(r.digitrust); err == nil {
digitrust := base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(digitrustBytes)
// Cookie name and structure are described here: https://github.com/digi-trust/dt-cdn/wiki/Cookies-for-Platforms
cookie = append(cookie, fmt.Sprintf("DigiTrust.v1.identity=%s", digitrust))
}
}
header.Set("Cookie", strings.Join(cookie, ";"))

return header
}
Expand All @@ -281,11 +353,16 @@ func parseAdformBids(response []byte) ([]*adformBid, error) {

func NewAdformBidder(client *http.Client, endpointURL string) *AdformAdapter {
a := &adapters.HTTPAdapter{Client: client}
var uriObj *url.URL
uriObj, err := url.Parse(endpointURL)
if err != nil {
panic(fmt.Sprintf("Incorrect Adform request url %s, check the configuration, please.", endpointURL))
}

return &AdformAdapter{
http: a,
URI: endpointURL,
version: "0.1.1",
URL: uriObj,
version: "0.1.2",
}
}

Expand Down Expand Up @@ -367,6 +444,40 @@ func openRtbToAdformRequest(request *openrtb.BidRequest) (*adformRequest, []erro
tid = request.Source.TID
}

gdprApplies := ""
var extRegs openrtb_ext.ExtRegs
if request.Regs != nil {
if err := json.Unmarshal(request.Regs.Ext, &extRegs); err != nil {
errors = append(errors, &adapters.BadInputError{
Message: err.Error(),
})
}
if extRegs.GDPR != nil && (*extRegs.GDPR == 0 || *extRegs.GDPR == 1) {
gdprApplies = strconv.Itoa(int(*extRegs.GDPR))
}
}

consent := ""
var digitrustData *openrtb_ext.ExtUserDigiTrust
if request.User != nil {
var extUser openrtb_ext.ExtUser
if err := json.Unmarshal(request.User.Ext, &extUser); err == nil {
consent = extUser.Consent
digitrustData = extUser.DigiTrust
}
}

var digitrust *adformDigitrust = nil
if digitrustData != nil {
digitrust = new(adformDigitrust)
digitrust.Id = digitrustData.ID
digitrust.Keyv = digitrustData.KeyV
digitrust.Version = 1
digitrust.Privacy = adformDigitrustPrivacy{
Optout: digitrustData.Pref != 0,
}
}

return &adformRequest{
adUnits: adUnits,
ip: getIPSafely(request.Device),
Expand All @@ -376,6 +487,9 @@ func openRtbToAdformRequest(request *openrtb.BidRequest) (*adformRequest, []erro
referer: referer,
userId: getBuyerUIDSafely(request.User),
tid: tid,
gdprApplies: gdprApplies,
consent: consent,
digitrust: digitrust,
}, errors
}

Expand Down
65 changes: 56 additions & 9 deletions adapters/adform/adform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,7 @@ func initTestData(server *httptest.Server, t *testing.T) (*AdformAdapter, contex

// prepare adapter
conf := *adapters.DefaultHTTPAdapterConfig
adapter := NewAdformAdapter(&conf, "adx.adform.net/adx")
adapter.URI = server.URL
adapter := NewAdformAdapter(&conf, server.URL)

prebidRequest := preparePrebidRequest(server.URL, t)
ctx := context.TODO()
Expand Down Expand Up @@ -214,6 +213,17 @@ func preparePrebidRequest(serverUrl string, t *testing.T) *pbs.PBSRequest {
if r.Bidders[0].BidderCode != "adform" {
t.Fatalf("ParsePBSRequest returned invalid bidder")
}

// can't be set in preparePrebidRequestBody as will be lost during json serialization and deserialization
// for the adapters which don't support OpenRTB requests the old PBSRequest is created from OpenRTB request
// so User and Regs are copied from OpenRTB request, see legacy.go -> toLegacyRequest
regs := getRegs()
r.Regs = &regs
user := openrtb.User{
Ext: getUserExt(),
}
r.User = &user

return r
}

Expand Down Expand Up @@ -246,6 +256,7 @@ func preparePrebidRequestBody(requestData aBidInfo, t *testing.T) *bytes.Buffer
},
}
}

body := new(bytes.Buffer)
err := json.NewEncoder(body).Encode(prebidRequest)
if err != nil {
Expand All @@ -258,8 +269,8 @@ func preparePrebidRequestBody(requestData aBidInfo, t *testing.T) *bytes.Buffer
// OpenRTB auction tests

func TestOpenRTBRequest(t *testing.T) {
bidder := new(AdformAdapter)
bidder.URI = "http://adx.adform.net"
bidder := NewAdformBidder(nil, "http://adx.adform.net")

testData := createTestData()
request := createOpenRtbRequest(testData)

Expand Down Expand Up @@ -334,6 +345,7 @@ func createOpenRtbRequest(testData *aBidInfo) *openrtb.BidRequest {
if testData.secure {
secure = int8(1)
}

bidRequest := &openrtb.BidRequest{
ID: "test-request-id",
Imp: make([]openrtb.Imp, len(testData.tags)),
Expand Down Expand Up @@ -361,6 +373,10 @@ func createOpenRtbRequest(testData *aBidInfo) *openrtb.BidRequest {
}
}

regs := getRegs()
bidRequest.Regs = &regs
bidRequest.User.Ext = getUserExt()

return bidRequest
}

Expand Down Expand Up @@ -452,6 +468,37 @@ func TestAdformProperties(t *testing.T) {

// helpers

func getRegs() openrtb.Regs {
var gdpr int8 = 1
regsExt := openrtb_ext.ExtRegs{
GDPR: &gdpr,
}
regs := openrtb.Regs{}
regsExtData, err := json.Marshal(regsExt)
if err == nil {
regs.Ext = regsExtData
}
return regs
}

func getUserExt() []byte {
digitrust := openrtb_ext.ExtUserDigiTrust{
ID: "digitrustId",
KeyV: 1,
Pref: 0,
}
userExt := openrtb_ext.ExtUser{
Consent: "abc",
DigiTrust: &digitrust,
}
userExtData, err := json.Marshal(userExt)
if err == nil {
return userExtData
}

return nil
}

func getPriceTypeString(priceType string) string {
if priceType != "" {
return fmt.Sprintf(", \"priceType\": \"%s\"", priceType)
Expand All @@ -469,7 +516,7 @@ func assertAdformServerRequest(testData aBidInfo, r *http.Request) *string {
return err
}
}
if ok, err := equal("CC=1&rp=4&fd=1&stid=transaction-id&ip=111.111.111.111&adid=6D92078A-8246-4BA4-AE5B-76104861E7DC&pt=gross&bWlkPTMyMzQ0&bWlkPTMyMzQ1&bWlkPTMyMzQ2", r.URL.RawQuery, "Query string"); !ok {
if ok, err := equal("CC=1&adid=6D92078A-8246-4BA4-AE5B-76104861E7DC&fd=1&gdpr=1&gdpr_consent=abc&ip=111.111.111.111&pt=gross&rp=4&stid=transaction-id&bWlkPTMyMzQ0&bWlkPTMyMzQ1&bWlkPTMyMzQ2", r.URL.RawQuery, "Query string"); !ok {
return err
}
if ok, err := equal("application/json;charset=utf-8", r.Header.Get("Content-Type"), "Content type"); !ok {
Expand All @@ -484,7 +531,7 @@ func assertAdformServerRequest(testData aBidInfo, r *http.Request) *string {
if ok, err := equal(testData.referrer, r.Header.Get("Referer"), "Referer"); !ok {
return err
}
if ok, err := equal(fmt.Sprintf("uid=%s", testData.buyerUID), r.Header.Get("Cookie"), "Buyer ID"); !ok {
if ok, err := equal(fmt.Sprintf("uid=%s;DigiTrust.v1.identity=eyJpZCI6ImRpZ2l0cnVzdElkIiwidmVyc2lvbiI6MSwia2V5diI6MSwicHJpdmFjeSI6eyJvcHRvdXQiOmZhbHNlfX0", testData.buyerUID), r.Header.Get("Cookie"), "Buyer ID"); !ok {
return err
}
return nil
Expand Down Expand Up @@ -528,9 +575,9 @@ func TestPriceTypeValidation(t *testing.T) {
func TestPriceTypeUrlParameterCreation(t *testing.T) {
// Arrange
priceTypeParameterTestCases := map[string][]*adformAdUnit{
"": {{MasterTagId: "123"}, {MasterTagId: "456"}},
"&pt=net": {{MasterTagId: "123", PriceType: priceTypeNet}, {MasterTagId: "456"}, {MasterTagId: "789", PriceType: priceTypeNet}},
"&pt=gross": {{MasterTagId: "123", PriceType: priceTypeNet}, {MasterTagId: "456", PriceType: priceTypeGross}, {MasterTagId: "789", PriceType: priceTypeNet}},
"": {{MasterTagId: "123"}, {MasterTagId: "456"}},
"net": {{MasterTagId: "123", PriceType: priceTypeNet}, {MasterTagId: "456"}, {MasterTagId: "789", PriceType: priceTypeNet}},
"gross": {{MasterTagId: "123", PriceType: priceTypeNet}, {MasterTagId: "456", PriceType: priceTypeGross}, {MasterTagId: "789", PriceType: priceTypeNet}},
}

// Act
Expand Down
19 changes: 17 additions & 2 deletions adapters/adform/adformtest/supplemental/user-nil.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,28 @@
}
}
}
]
],
"regs": {
"ext": {
"gdpr": 1
}
},
"user": {
"ext": {
"consent": "abc2",
"digitrust": {
"ID": "digitrustId",
"KeyV": 1,
"Pref": 0
}
}
}
},

"httpCalls": [
{
"expectedRequest": {
"uri": "http://adx.adform.net/adx/?CC=1&rp=4&fd=1&stid=&ip=&pt=gross&bWlkPTE"
"uri": "http://adx.adform.net/adx?CC=1&fd=1&gdpr=1&gdpr_consent=abc2&ip=&pt=gross&rp=4&stid=&bWlkPTE"
},
"mockResponse": {
"status": 204
Expand Down
Loading

0 comments on commit c6578cd

Please sign in to comment.